UOJ422 【集训队作业2018】小Z的礼物
Description
小Z有一个神奇的自动售货机,里面有$n\times m$种物品,分别放在$n$行$m$列个格子中。每当小Z向自动售货机中投入一枚硬币,他就能获得一对相邻格子中的物品(已经获得的物品可能再次获得),获得每一对相邻格子中的物品的概率是相等的。在这$n\times m$种物品中,有一些物品是小Z喜欢的(小Z喜欢的用 * [星号] 表示,其他的用 .[英文句号] 表示),他想把这些物品包装成一份礼物。小Z想知道,期望投入多少枚硬币后,就可以获得这些他喜欢的物品。
Solution
在一个$n\times m$的网格中随机选取一个$1\times 2$的区域染色,求将给定的点全部染色的期望染色次数
先Min-Max容斥一下,就变成了要求对于每个点集任意覆盖一个点的期望次数
可算出$E(T)=\frac{2mn-m-n}{x}$,$x$为点集中点的个数
但是点集的数量大的飞起,所以设$dp_{i,j,s,cnt}$为考虑到格子$(i,j)$,轮廓线左侧的$n$个格子的选取情况为$s$,$x$值为$cnt$的所有点集的容斥系数之和
每个染色区域在扫到其上方/左方格子时就记一次数,每个格子分不染色和染色两种情况讨论
挺毒的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma GCC optimize(2) #include<iostream> #include<cstring> #include<cstdio> using namespace std; long long n,m,dp[2][70][1205],inv[1205],sum,pre,cur=1,ans; const long long mod=998244353; char map[10][105]; inline long long read() { long long w=0,f=1; char ch=0; while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { w=(w<<1)+(w<<3)+ch-'0'; ch=getchar(); } return w*f; } int main() { n=read(); m=read(); sum=2*n*m-n-m; inv[0]=inv[1]=1; for(long long i=2;i<=sum;i++) { inv[i]=inv[mod%i]*(mod-mod/i)%mod; } for(long long i=1;i<=n;i++) { scanf("%s",map[i]+1); } dp[cur][0][0]=mod-1; for(long long i=1;i<=m;i++) { for(long long j=1;j<=n;j++) { swap(cur,pre); memset(dp[cur],0,sizeof(dp[cur])); for(long long k=0;k<(1<<n);k++) { for(long long l=0;l<=sum;l++) { long long now=k&(((1<<n)-1)^(1<<(j-1))); (dp[cur][now][l]+=dp[pre][k][l])%=mod; if(map[j][i]=='*') { now|=1<<(j-1); long long cnt=0; if(i>1&&!((1<<(j-1))&k)) { ++cnt; } if(j>1&&!((1<<(j-2))&k)) { ++cnt; } if(i<m) { ++cnt; } if(j<n) { ++cnt; } (dp[cur][now][l+cnt]+=mod-dp[pre][k][l])%=mod; } } } } } for(long long i=0;i<(1<<n);i++) { for(long long j=1;j<=sum;j++) { (ans+=dp[cur][i][j]*inv[j])%=mod; } } printf("%lld\n",ans*sum%mod); return 0; }