2019.2.25 模拟赛T1【集训队作业2018】小Z的礼物

T1:

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

我们发现我们要求的是覆盖所有集合里的元素的期望时间。
\(t_{i,j}\)表示第一次覆盖第i行第j列的格子的时间,我们要求的是\(max\{ALL\}\)
考虑\(min-max容斥\)\(max\{S\}=\sum_{S \subset T}(-1) ^{|T|-1}min\{T\}\)
此时我们要求的变为了\(min\{T\}\),即\(T\)中至少有一个元素被选择的期望。
我们知道当\(T\)中元素被选择的概率为\(P\)时,其期望为\(\frac{1}{P}\)
因此我们只需要求出符合条件的方案数就行了。
注意到n很小,所以考虑状压轮廓线。
\(f_{j,k}\)表示轮廓线为\(j\),方案数为\(k\)的集合个数。
枚举每个i,j是否选入集合并考虑其周围的选择情况转移即可。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define mod 998244353
#define ll long long
using namespace std;
inline int read() {
	int x=0,f=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
	return x*f;
}
ll n,m,f[2][200][2000],sum;
char s[10][200];
ll power(ll x,int p) {
	ll ans=1;
	while(p) {
		if(p&1) ans*=x,ans%=mod;
		x*=x;x%=mod;p>>=1;
	}
	return ans;
}
int main() {
	n=read(),m=read();
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	int pre=0;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) sum+=(i>1)+(j>1);
	f[pre][0][0]=mod-1;
	for(int i=1;i<=m;i++) {
		for(int j=1;j<=n;j++) {
			for(int S=0;S<(1<<n);S++) {
				for(int k=0;k<=sum;k++) {
					if(!f[pre][S][k]) continue;
					int tmp=pre^1;
					if(s[j][i]=='*') {
						int to=S|(1<<(j-1));
						f[tmp][to][k+(j>1&&!((S>>(j-2))&1))+(i>1&&!((S>>(j-1))&1))+(i<m)+(j<n)]+=-1*f[pre][S][k];
						f[tmp][to][k+(j>1&&!((S>>(j-2))&1))+(i>1&&!((S>>(j-1))&1))+(i<m)+(j<n)]%=mod;
						f[tmp][to][k+(j>1&&!((S>>(j-2))&1))+(i>1&&!((S>>(j-1))&1))+(i<m)+(j<n)]+=mod;
						f[tmp][to][k+(j>1&&!((S>>(j-2))&1))+(i>1&&!((S>>(j-1))&1))+(i<m)+(j<n)]%=mod;
						
					}
					int to=S&(((1<<n)-1)^(1<<(j-1)));
					f[tmp][to][k]+=f[pre][S][k];
					f[tmp][to][k]%=mod;
					f[pre][S][k]=0;
				} 
			}
			pre^=1;
		}
	}
	ll ans=0;
	for(int i=0;i<=sum;i++){
		int tot=0;
		for(int j=0;j<(1<<n);j++) {tot+=f[pre][j][i],tot%=mod;}
		ans=(ans+tot*power(i,mod-2))%mod;
	}
	ans=ans*sum%mod;
	printf("%lld\n",ans);
}
posted @ 2019-03-05 09:04  wls001  阅读(209)  评论(0编辑  收藏  举报