[UOJ422]小Z的礼物

时间:2022-06-27 18:41:38

设要取的物品集合为$S$,$E=n(m-1)+(n-1)m$,$x_T$为覆盖了$T$中至少一个元素的$1\times2$数量

$$\begin{aligned}\sum\limits_{i=1}^\infty i[恰好i次]&=\sum\limits_{i=1}^\infty[\geq i次]\\&=\sum\limits_{i=0}^\infty[i次后未成功]\\&=\sum\limits_{i=0}^\infty\sum\limits_{\substack{T\subseteq S\\T\ne\varnothing}}(-1)^{|T|+1}\left(1-\frac{x_T}E\right)^i\\&=E\sum\limits_{\substack{T\subseteq S\\T\ne\varnothing}}(-1)^{|T|+1}\frac1{x_T}\end{aligned}$$

所以要对每个$1\leq k\leq E$计数有多少个大小为奇数/偶数的集合$T$满足$x_T=k$

考虑按列-行做轮廓线DP,设$f_{i,j,k,0/1}$表示当前DP到第$i$个格子,当前轮廓线上“=*且在$T$中”的状态为$j$,$x_T=k$,$|T|\equiv0/1\pmod2$的$T$的数量

时间复杂度$O(n^2m^22^n)$

#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=998244353;
int mul(int a,int b){return(ll)a*b%mod;}
void inc(int&a,int b){(a+=b)>=mod?a-=mod:0;}
int de(int a,int b){return(a-=b)<0?a+mod:a;}
int pow(int a,int b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
char s[10][110];
int n,m,E;
int inv[1210],f[64][1210][2],g[64][1210][2];
int is(int x,int k){
	return k>=0?x>>k&1:0;
}
int main(){
	int i,j,k,l,u,v,res;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)scanf("%s",s[i]+1);
	E=n*(m-1)+(n-1)*m;
	inv[1]=1;
	for(i=2;i<=E;i++)inv[i]=mul(mod/i,mod-inv[mod%i]);
	f[0][0][0]=1;
	for(j=1;j<=m;j++){
		for(i=1;i<=n;i++){
			memset(g,0,sizeof(g));
			for(k=0;k<1<<n;k++){
				for(l=0;l<=E;l++){
					if(!(f[k][l][0]|f[k][l][1]))continue;
					u=k&~(1<<(i-1));
					v=l+is(k,i-1)+is(k,i-2);
					inc(g[u][v][0],f[k][l][0]);
					inc(g[u][v][1],f[k][l][1]);
					if(s[i][j]=='*'){
						u=k|(1<<(i-1));
						v=l+(i>1)+(j>1);
						inc(g[u][v][0],f[k][l][1]);
						inc(g[u][v][1],f[k][l][0]);
					}
				}
			}
			memcpy(f,g,sizeof(g));
		}
	}
	res=0;
	for(i=1;i<=E;i++){
		u=v=0;
		for(j=0;j<1<<n;j++){
			inc(u,f[j][i][1]);
			inc(v,f[j][i][0]);
		}
		inc(res,mul(inv[i],de(u,v)));
	}
	printf("%d",mul(res,E));
}