做题笔记,CF 4.12Div2 E CF1511E Colorings and Dominoes(思维+DP)
题目大意
有一个长得像下面这样的图(好丑 ):
1000
1000
0010
0000
1000\\1000\\0010\\0000
1000100000100000
1.1表示可以放砖,0不能(0,1反过来也无所谓,理解意思就行)
2.而你可以在每个1的位置放红色或蓝色砖
3.你可以在连续竖着的2个红色位置上放一块骨牌,或者连续横着的两个蓝色位置上放一块骨牌
4.显然,有多少的1,就有2的多少次方的染色方案种数
5.你的任务是对于每种染色方案,记录其最大放骨牌数,对所有染色方案加和
6.图的长宽的乘积小于3e5
做法
1.假如我们当前的染色方案确定的话,我们每个位置只能放横着或者竖着的骨牌,受颜色限制,只能是一种。所以对于所有的染色方案,我们可以把他对答案的贡献拆成两部分,一部分是横着的,一部分是竖着的,这样我们就可以每行或者每列分开求
2.我们可以用一个dp数组来存放长度为i的直链所有支持的放牌方案数下的最大骨牌数和
考虑下面这样一行(列同理)
1
1
1
emmm…它大概是不太行,所以
d
p
[
1
]
=
0
dp[1]=0
dp[1]=0
那么下面这一行
11
11
11
它只有在都涂蓝的时候可以放一块横的骨牌,所以
d
p
[
2
]
=
1
dp[2]=1
dp[2]=1
同理,
d
p
[
3
]
=
4
dp[3]=4
dp[3]=4
那么对于
11111111111111...111
11111111111111...111
11111111111111...111
我们考虑他的最后2位(
a
[
i
]
和
a
[
i
−
1
]
a[i]和a[i-1]
a[i]和a[i−1])
①.假如
a
[
i
−
1
]
=
红
a[i-1]=红
a[i−1]=红那么必然i位置不管是蓝红都放不下新的骨牌
d
p
[
i
]
=
d
p
[
i
−
2
]
∗
2
dp[i]=dp[i-2]*2
dp[i]=dp[i−2]∗2
②.假如
a
[
i
−
1
]
=
蓝
a[i-1]=蓝
a[i−1]=蓝且
a
[
i
]
=
蓝
a[i]=蓝
a[i]=蓝,那么不仅新的位置可以贪心的放一块,一定最优,对于之前
i
−
2
i-2
i−2位置的最优方案,那么每种方案答案加1,由于之前位置的染色方案显然有
2
i
−
1
2^{i-1}
2i−1种,此时
d
p
[
i
]
=
d
p
[
i
−
2
]
+
2
i
−
2
dp[i]=dp[i-2]+2^{i-2}
dp[i]=dp[i−2]+2i−2
③.假如
a
[
i
−
1
]
=
蓝
a[i-1]=蓝
a[i−1]=蓝且
a
[
i
]
=
红
a[i]=红
a[i]=红,那么,我们考虑下面的过程
d
p
[
i
−
1
]
dp[i-1]
dp[i−1]应该是
i
−
1
i-1
i−1位置上放蓝和红的和,那么放红时,此时贡献为
d
p
[
i
−
2
]
dp[i-2]
dp[i−2]
所以放蓝时的贡献(也就是我们现在
d
p
[
i
]
dp[i]
dp[i]应该被更新的值)应该是
d
p
[
i
]
=
d
p
[
i
−
1
]
−
d
p
[
i
−
2
]
dp[i]=dp[i-1]-dp[i-2]
dp[i]=dp[i−1]−dp[i−2]
综上,总的更新式为
d
p
[
i
]
=
d
p
[
i
−
2
]
∗
2
+
d
p
[
i
−
1
]
+
2
i
−
2
dp[i]=dp[i-2]*2+dp[i-1]+2^{i-2}
dp[i]=dp[i−2]∗2+dp[i−1]+2i−2
以上是一条链对于答案的贡献
3.考虑每条横的直链,假设有总共tot个可染色的点,链的长度为l,那么考虑除了这条链外其他的染色方案,由于这条链的存在,每种方案的答案都会增加
d
p
[
l
]
dp[l]
dp[l],显然其他的染色方案有
2
t
o
t
−
l
2^{tot-l}
2tot−l种,所以每条链的贡献为
d
p
[
l
]
∗
2
t
o
t
−
l
dp[l]*2^{tot-l}
dp[l]∗2tot−l
然后我们统计所有的横链即可
4.竖链道理一样,再竖着求一遍就行了
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m;
vector<int> a[300005];
char s[300005];
ll p[300005];
ll dp[300005];
ll mod=998244353;
ll ans=0;
int tot=0;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++){
if(s[j]=='o') a[i].push_back(1),tot++;
else{
a[i].push_back(0);
}
}
}
p[0]=1;
for(int i=1;i<=n*m;i++){
p[i]=p[i-1]*2;p[i]%=mod;
}
//for(int i=1;i<=n*m;i++) cout<<p[i]<<" ";
dp[1]=0,dp[2]=1,dp[3]=3;
for(int i=4;i<=n*m;i++){
dp[i]=dp[i-1]+2*dp[i-2]+p[i-2];
dp[i]%=mod;
}
for(int i=1;i<=n;i++){
int now=0;
for(int j=0;j<m;j++){
if(a[i][j]) now++;
else{
if(now>=2){
ans+=(dp[now]*p[tot-now])%mod;
ans%=mod;
}
now=0;
}
}
if(now>=2) ans+=(dp[now]*p[tot-now])%mod;ans%=mod;
}
for(int i=0;i<m;i++){
int now=0;
for(int j=1;j<=n;j++){
if(a[j][i]) now++;
else{
if(now>=2){
ans+=(dp[now]*p[tot-now])%mod;
ans%=mod;
}
now=0;
}
}
if(now>=2) ans+=(dp[now]*p[tot-now])%mod;ans%=mod;
}
printf("%lld",ans);
}