UOJ422 【集训队作业2018】小Z的礼物

题目

不难发现我们要求的是一个\(E(\max(S))\),这看起来比较困难,于是我们直接上min-max容斥,如果我们枚举了一个集合\(T\),集合\(T\)中有\(t\)对相邻格子,那么对答案的贡献就是\((-1)^{|T|+1}\frac{2nm-n-m}{t}\)

于是我们搞一个状压轮廓线的dp,设\(dp_{i,j,s,k}\)表示当前处理的格子是\((i,j)\),轮廓线状态为\(s\),选出了\(k\)对相邻格子,刷表转移即可

代码

#include<bits/stdc++.h>
#define re register
const int mod=998244353;
inline int dqm(int x) {return x<0?x+mod:x;}
inline int upd(int &x,int y) {x+=y;if(x>=mod)x-=mod;}
int dp[2][(1<<6)+1][1200],inv[1200],ans,n,m;
char ma[105][105];
int main() {
	scanf("%d%d",&n,&m);int S=2*n*m-n-m,nS=0,len=(1<<n);--len;inv[1]=1;
	for(re int i=2;i<=S;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(re int i=1;i<=n;i++) scanf("%s",ma[i]+1);
	dp[0][0][0]=mod-1;int o=0;
	for(re int i=1;i<=m;i++)
		for(re int j=1;j<=n;j++) {
			memset(dp[o^1],0,sizeof(dp[o^1]));
			for(re int s=0;s<=len;++s)
				for(re int k=0;k<=nS;++k) {
					if(!dp[o][s][k]) continue;
					upd(dp[o^1][s&(len^(1<<(j-1)))][k],dp[o][s][k]);
					if(ma[j][i]=='*') {
						int det=0;
						if(i>1&&!(s>>(j-1)&1)) det++;
						if(j>1&&!(s>>(j-2)&1)) det++;
						if(j<n) ++det;if(i<m) ++det; 
						upd(dp[o^1][s|(1<<(j-1))][k+det],dqm(mod-dp[o][s][k]));
						if(k+det>nS) nS=k+det;
					}
				}
			o^=1;
		} 
	int ans=0;
	for(re int s=0;s<=len;++s)
		for(re int k=0;k<=S;++k) upd(ans,1ll*dp[o][s][k]*inv[k]%mod);
	printf("%d\n",1ll*ans*S%mod);return 0;
}
posted @ 2019-12-26 15:42  asuldb  阅读(218)  评论(0编辑  收藏  举报