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);
}