#L3111. SDOI2019」染色

SDOI2019」染色

为了解决这个问题,我们需要计算一个2行n列网格的合法染色方案数,其中一些格子已经染色,其余格子需要染色,且相邻格子颜色不能相同。由于网格规模可能很大,我们采用动态规划结合数学优化的方法来高效处理。

方法思路

  1. 问题分析:网格有2行n列,某些格子已染色,其余需染色。要求相邻格子(上下左右)颜色不同。直接枚举所有可能颜色组合不可行,需优化。
  2. 关键思路:对于小颜色数(c ≤ 100),使用动态规划(DP)记录每列颜色对状态,通过预处理和优化转移条件来减少计算量。对于大颜色数,假设所有列均为未染色,使用数学公式计算方案数。
  3. 算法选择
    • 小c DP:使用滚动数组和预处理技术,将转移复杂度从O(c^4)降至O(c^2)。
    • 大c段处理:将网格分段,每段连续未染色列方案数为(c^2 - 3c + 3)^L,其中L为段长度。
  4. 复杂度分析
    • 小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;
}

代码解释

  1. 输入处理:读取网格大小和颜色数,检查是否存在相邻格子颜色相同的情况。
  2. 小c DP
    • 初始化:第一列的颜色对方案数,考虑已染色格子的限制。
    • 转移:通过预处理总和、行和、列和,优化状态转移。
    • 结果计算:累加最后一列所有合法颜色对的方案数。
  3. 大c段处理:计算连续未染色段的方案数,使用数学公式(c^2 - 3c + 3)^L。
  4. 输出:打印合法方案数,对结果取模。