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
",res);
}
原文地址:https://www.cnblogs.com/whitelily/p/14651879.html