Codeforces 1511 E. Colorings and Dominoes(DP)

传送门


题目大意

给出一个棋盘,黑格代表 ∗ * ,白格代表 o o o,其中白格可以被染成红色或者蓝色。对于横着的连续的两个红格可以放一个骨牌,对于竖着的连续的两个蓝格可以放一个骨牌。设白格有 w w w个,那么对于所有的 2 w 2^w 2w个染色方案中,每种方案都放置最多的骨牌,问所有方案中所有骨牌的数量之和是多少。

解题思路

关键点一

可以将问题从 每个方案的数量和 转化为 求每个骨牌所存在的方案数,然后找到所有能放的骨牌,把每个骨牌的方案数加起来。有了这个之后接下来就解决最优放置的问题,实际上只要对于一排(或者一列)连续的白色格子,我们在这一排(或者一列)通过染色放置的骨牌是最优的情况,那么剩下的点染什么色放什么骨牌是剩下的点要考虑的事情,而每一种染色方案都一定可以得到一个对于那个方案的最优解,那样的解是无法影响我当前这连续的一排(或一列)格子的放置方案;对于行和列交错的格子,该格子被染成不同的颜色会分别对行和列产生影响,但这个影响是对行列互不影响的。所以这个题目就又被拆分成如下情况:当前有一排(或一列)连续的白色格子共 k k k 个,那么这 k k k 个格子对答案产生的贡献就是 c a l ( k ) ∗ 2 w − k cal(k)*2^{w-k} cal(k)2wk,我们只要分行和列分别讨论就可以得到结果。

关键点二

然后就是需要考虑对于一行(一列)如何求解放置的最多骨牌的数量,设 d [ i ] d[i] d[i]代表长度为 i i i的连续白格能放置的最多骨牌数:

在这里插入图片描述

  • 若第 n n n格被染成蓝色,那么这个位置无论如何也放置不了,故 d [ n ] + = d [ n − 1 ] d[n] += d[n-1] d[n]+=d[n1]
  • 若第 n n n格被染成红色,那么该格子能否被填充取决于第 n − 1 n-1 n1个格子的颜色:
    • 若第 n − 1 n-1 n1个格子是蓝色,那么显然两个格子无法放置新的骨牌,因此 d [ n ] + = d [ n − 2 ] d[n] += d[n-2] d[n]+=d[n2]
    • 若第 n − 1 n-1 n1个格子是红色,除了前 n − 2 n-2 n2个位置的贡献,最后两个格子能单独放一个骨牌,那么新增的这一部分的贡献为前面 n − 2 n-2 n2个位置的所有方案总数即 2 n − 2 2^{n-2} 2n2,故 d [ n ] + = d [ n − 2 ] + 2 n − 2 d[n] += d[n-2] + 2^{n-2} d[n]+=d[n2]+2n2

综上,DP转移方程为 d [ n ] = d [ n − 1 ] + d [ n − 2 ] ∗ 2 + 2 n − 2 d[n] = d[n-1] + d[n-2] * 2 + 2^{n-2} d[n]=d[n1]+d[n2]2+2n2,初始边界为 d [ 0 ] = d [ 1 ] = 0 d[0] = d[1] = 0 d[0]=d[1]=0

那么对每一行每一列连续的白格单独讨论然后累加即可。

#include <bits/stdc++.h>

using namespace std;
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
const int Mod = 998244353;
const int maxn = 3e5 + 10;

ll d[maxn], b[maxn];

void init() {
    
    
    b[0] = 1;
    for (int i = 1; i < maxn; i++) b[i] = b[i - 1] * 2 % Mod;
    d[0] = d[1] = 0;
    for (int i = 2; i < maxn; i++) d[i] = ((d[i - 1] + d[i - 2] * 2) % Mod + b[i - 2]) % Mod;
}

vector<vector<char>> g;

int main() {
    
    
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    init();
    cin >> n >> m;
    g.resize(n + 1);
    for (int i = 1; i <= n; i++) g[i].resize(m + 1);
    int sum = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
    
    
            cin >> g[i][j];
            if (g[i][j] == 'o') sum++;
        }
    ll ans = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
    
    
            int k = j;
            while (j <= m && g[i][j] == 'o') j++;
            ans = (ans + d[j - k] * b[sum - j + k] % Mod) % Mod;
        }
    for (int j = 1; j <= m; j++)
        for (int i = 1; i <= n; i++) {
    
    
            int k = i;
            while (i <= n && g[i][j] == 'o') i++;
            ans = (ans + d[i - k] * b[sum - i + k] % Mod) % Mod;
        }
    cout << ans << ENDL;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/115741759