Educational Codeforces Round 107 (Rated for Div. 2)
Educational Codeforces Round 107 (Rated for Div. 2)
E. Colorings and Dominoes
题意:
给一个n * m的矩阵,o表示可以安排为红格子或蓝格子,然后在将一个1 * 2大小的小矩阵放入大矩阵中,横着放只能放在两个蓝格子里,竖着放只能放在两个红格子里。
求所有安排的最大可放小矩阵数的和。
题解:
一开始看到这个题,感觉要用最小割求一个方案中的最大小矩阵数,之后想了一下,因为竖着放只能放在红格子中,横着只能在蓝格子中,所以同一个格子并不会有我到底是竖着放还是横着放的取法,也就意味的没有抉择,且每一行每一列的贡献都是独立的。
所以,我们可以把贡献拆分,每次只算每一列,每一行的贡献求和即可。
那么一条的贡献怎么求,可以用状态机dp,一维存枚举到哪个位置,一维存上个点是否可以放小矩阵 ,当s[i]=='o'时,我便可以让他取红格子或蓝格子。其中一种,可以让第2维加+1,或者上个点可以放小矩阵我直接加贡献,其贡献为,2的未遍历到的所有0的数次方。
#include<iostream>
#include<vector>
using namespace std;
#define ll long long
const ll N=3e5+7;
const ll mod=998244353;
ll n,m,tot;
string s[N],t[N];
ll dp[N][2],cnt[N],pw[N];
vector<ll>ho;
void init(int n,string s){
ho.clear();
for(int i=0;i<n;i++){
dp[i][0]=dp[i][1]=-1;
if(i==0)cnt[i]=tot;
else cnt[i]=cnt[i-1];
if(s[i]=='o'){
ho.push_back(1);
cnt[i]--;
}
else{
ho.push_back(0);
}
}
}
ll dfs(ll p,ll k){
if(p==ho.size()){
return 0;
}
if(dp[p][k]!=-1){
return dp[p][k];
}
ll now=ho[p];
ll sum=0;
if(now==1){
sum+=dfs(p+1,0);
if(k==1){
sum+=(dfs(p+1,0)+pw[cnt[p]])%mod;
sum%=mod;
}
else{
sum+=dfs(p+1,1);
sum%=mod;
}
}
else{
sum+=dfs(p+1,0);
sum%=mod;
}
return dp[p][k]=sum;
}
ll pow(ll x,ll n,ll mod)
{
ll res=1;
while(n>0){
if(n%2==1){
res=res*x;
res=res%mod;
}
x=x*x;
x=x%mod;
n>>=1;
}
return res;
}
void solve(){
for(int i=0;i<N;i++){
pw[i]=pow(2,i,mod);
}
}
int main(){
solve();
scanf("%lld%lld",&n,&m);
for(int i=0;i<n;i++){
cin>>s[i];
}
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(s[j][i]=='o')tot++;
t[i]+=s[j][i];
}
}
ll res=0;
for(int i=0;i<n;i++){
init(m,s[i]);
res+=dfs(0,0);
res%=mod;
}
for(int i=0;i<m;i++){
init(n,t[i]);
res+=dfs(0,0);
res%=mod;
}
printf("%lld\n",res);
}