题目
题目链接:http://codeforces.com/problemset/problem/1511/E
你有一个大矩形板子分成了 (n imes m) 个格子,每个格子上的颜色为黑色(*
)和白色(o
)
你可以给每个白色格子染成红色或蓝色,那么显然有 (2^w)种染色方案((w) 为白色格子数量)
对于每一种染色方案,你要尽可能往上放大小为 (1 imes2) 的多米诺骨牌,规则是:
- 横着的骨牌必须在两个红色格子上
- 竖着的骨牌必须在两个蓝色格子上
现在想要计算 “每种涂色方案的最多骨牌摆放数量”之和。
这个数可能非常大,输出对 (998\,244\,353) 取余后的结果即可。
(n imes mleq 3 imes 10^5)。
思路
不难发现横竖的格子是互不影响的,所以我们只需要把横着和竖着的每个极长连续白色格子单独拿出来计算贡献即可。
假设一个极长连续白色格子的长度为 (k),那么其贡献就是 (2^{ ext{cnt}-k} imes h[k]),其中 ( ext{cnt}) 是总共的白色格子数量,(h[k]) 是长度为 (k) 的连续白色格子上色的所有方案中,每种涂色方案的最多骨牌摆放数量之和。
考虑如何求出 (h[k])。我们设 (f[i][0/1/2]) 表示长度为 (i) 的连续白色格子第 (i) 个位置不让放 / 可以放但是没有放 / 可以放且放了的所有方案中,每种涂色方案的最多骨牌摆放数量之和。(g[i][0/1/2]) 表示能转移到 (f[i][0/1/2]) 的方案数。
那么转移的时候讨论一下最后一个位置是怎么搞就好了。最后 (h[i]=f[i][0]+f[i][1]+f[i][2])。
然后枚举所有极长连续白色格子统计答案即可。
时间复杂度 (O(nm))。
代码
#include <bits/stdc++.h>
#define mp make_pair
#define ST first
#define ND second
using namespace std;
typedef long long ll;
typedef long double ld;
const int N=300010,MOD=998244353;
int Q,n,m,ans,cnt,f[N][3],g[N][3],pw[N];
int main()
{
// freopen("data.txt","r",stdin);
pw[0]=1; g[0][0]=1;
for (int i=1;i<N;i++)
{
pw[i]=pw[i-1]*2%MOD;
f[i][0]=((f[i-1][0]+f[i-1][1])%MOD+f[i-1][2])%MOD;
g[i][0]=((g[i-1][0]+g[i-1][1])%MOD+g[i-1][2])%MOD;
f[i][1]=(f[i-1][0]+f[i-1][2])%MOD;
g[i][1]=(g[i-1][0]+g[i-1][2])%MOD;
f[i][2]=(f[i-1][1]+g[i-1][1])%MOD;
g[i][2]=g[i-1][1];
}
scanf("%d%d",&n,&m);
for (int i=1;i<N;i++)
f[i][0]=((f[i][0]+f[i][1])%MOD+f[i][2])%MOD;
char ch[n+5][m+5];
for (int i=0;i<n+5;i++)
for (int j=0;j<m+5;j++)
ch[i][j]='-';
for (int i=1;i<=n;i++)
{
scanf("%s",ch[i]+1);
for (int j=1;j<=m;j++)
if (ch[i][j]=='o') cnt++;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (ch[i][j]=='o' && ch[i][j-1]!='o')
{
int k=j;
while (ch[i][k+1]=='o') k++;
k=k-j+1;
ans=(ans+1LL*f[k][0]*pw[cnt-k])%MOD;
}
if (ch[i][j]=='o' && ch[i-1][j]!='o')
{
int k=i;
while (ch[k+1][j]=='o') k++;
k=k-i+1;
ans=(ans+1LL*f[k][0]*pw[cnt-k])%MOD;
}
}
printf("%d",ans);
return 0;
}