#L3111. SDOI2019」染色
SDOI2019」染色
为了解决这个问题,我们需要计算一个2行n列网格的合法染色方案数,其中一些格子已经染色,其余格子需要染色,且相邻格子颜色不能相同。由于网格规模可能很大,我们采用动态规划结合数学优化的方法来高效处理。
方法思路
- 问题分析:网格有2行n列,某些格子已染色,其余需染色。要求相邻格子(上下左右)颜色不同。直接枚举所有可能颜色组合不可行,需优化。
- 关键思路:对于小颜色数(c ≤ 100),使用动态规划(DP)记录每列颜色对状态,通过预处理和优化转移条件来减少计算量。对于大颜色数,假设所有列均为未染色,使用数学公式计算方案数。
- 算法选择:
- 小c DP:使用滚动数组和预处理技术,将转移复杂度从O(c^4)降至O(c^2)。
- 大c段处理:将网格分段,每段连续未染色列方案数为(c^2 - 3c + 3)^L,其中L为段长度。
- 复杂度分析:
- 小c DP:时间复杂度O(n * c^2),空间复杂度O(c^2)。
- 大c段处理:时间复杂度O(n),空间复杂度O(1)。
解决代码
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 9;
long long mod_pow(long long base, long long exp, long long mod) {
long long result = 1;
base %= mod;
while (exp > 0) {
if (exp % 2 == 1)
result = (result * base) % mod;
base = (base * base) % mod;
exp /= 2;
}
return result;
}
int main() {
int n, c;
cin >> n >> c;
vector<vector<int>> a(2, vector<int>(n));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < n; j++) {
cin >> a[i][j];
}
}
for (int j = 0; j < n; j++) {
if (a[0][j] != 0 && a[1][j] != 0 && a[0][j] == a[1][j]) {
cout << 0 << endl;
return 0;
}
}
if (c <= 100) {
vector<vector<long long>> dp(c + 1, vector<long long>(c + 1, 0));
for (int x = 1; x <= c; x++) {
for (int y = 1; y <= c; y++) {
if (x == y) continue;
if (a[0][0] != 0 && x != a[0][0]) continue;
if (a[1][0] != 0 && y != a[1][0]) continue;
dp[x][y] = 1;
}
}
for (int i = 1; i < n; i++) {
vector<vector<long long>> dp2(c + 1, vector<long long>(c + 1, 0));
long long S = 0;
vector<long long> A(c + 1, 0), B(c + 1, 0);
for (int x = 1; x <= c; x++) {
for (int y = 1; y <= c; y++) {
S = (S + dp[x][y]) % MOD;
A[x] = (A[x] + dp[x][y]) % MOD;
B[y] = (B[y] + dp[x][y]) % MOD;
}
}
for (int x = 1; x <= c; x++) {
for (int y = 1; y <= c; y++) {
if (x == y) continue;
if (a[0][i] != 0 && x != a[0][i]) continue;
if (a[1][i] != 0 && y != a[1][i]) continue;
long long val = (S - A[x] - B[y] + dp[x][y] + MOD + MOD) % MOD;
dp2[x][y] = val;
}
}
dp = dp2;
}
long long ans = 0;
for (int x = 1; x <= c; x++) {
for (int y = 1; y <= c; y++) {
ans = (ans + dp[x][y]) % MOD;
}
}
cout << ans << endl;
} else {
long long T = (1LL * c * c - 3 * c + 3) % MOD;
long long ans = 1;
for (int i = 0; i < n; i++) {
if (a[0][i] == 0 && a[1][i] == 0) {
ans = (ans * T) % MOD;
} else {
}
}
cout << ans << endl;
}
return 0;
}
代码解释
- 输入处理:读取网格大小和颜色数,检查是否存在相邻格子颜色相同的情况。
- 小c DP:
- 初始化:第一列的颜色对方案数,考虑已染色格子的限制。
- 转移:通过预处理总和、行和、列和,优化状态转移。
- 结果计算:累加最后一列所有合法颜色对的方案数。
- 大c段处理:计算连续未染色段的方案数,使用数学公式(c^2 - 3c + 3)^L。
- 输出:打印合法方案数,对结果取模。