AGC056E-Cheese【dp】

前言

奶酪可能会长腿,但绝对不会变质
_ _,_ _ _ _ _ _ _


正题

题目链接:https://atcoder.jp/contests/agc056/tasks/agc056_e


题目大意

有一个长度为\(n\)的环,第\(i+0.5(0\leq i<n)\)位置上各有一只老鼠。

然后进行以下操作\(n-1\)次:
\(i\)个位置有\(a_i\%\)的概率被选择(选择一个),然后在这个位置上放一个奶酪,这个奶酪会顺时针跑,跑到一个老鼠的位置时有\(50\%\)的概率被吃掉,老鼠如果吃过奶酪就会离开。

求每只老鼠被留到最后的概率。

\(1\leq n\leq 50,\sum_{i=0}^{n-1}a_i=100\)


解题思路

考虑第\(n\)只老鼠留到最后的概率,因为无论是哪只奶酪吃什么老鼠都是一样的,所以我们可以考虑开始时就将所有的奶酪放下来,然后让它们一起绕着环走。

同样的,奶酪的移动顺序也是无关紧要的,所以我们可以考虑让所有的奶酪先绕道第\(n\)只老鼠的面前然后一起统计。

那么先考虑\(dp\),设\(f_{i,j,k}\)表示现在已经放下的奶酪都已经走到了位置\(i\)处,然后已经放下了\(j\)个奶酪,已经有\(k\)个被老鼠吃了。

那么每次的转移分成两部分,第一部分是放奶酪,枚举这个位置放置了\(x\)个奶酪,需要注意的是除了概率以外因为这个方案是涉及可重排列的,所以还需要乘上一个\(\frac{1}{x!}\)

第二部分是吃奶酪,因为每只奶酪只会吃一个老鼠,所以统计没有这些奶酪都没有被吃的概率,很方便转移。

那么假设目前有\(k\)个奶酪走到了第\(n\)只老鼠前,那么说明前面还剩下\(k-1\)只老鼠没有被吃,那么考虑这种情况下第\(n\)只老鼠被留到最后的概率。

考虑一个神必的证明方法,我们考虑绕一圈绕到第\(i\)只老鼠处还剩下\(x\)个奶酪,然后考虑\(i\)\(i+1\)之间被留到最后的概率:

  • 如果\(i\)\(i+1\)都吃到了,我们显然得不出两个的概率关系。
  • 如果\(i\)\(i+1\)都没被吃到,我们也得不出来,考虑下一轮。
  • 如果\(i\)吃了并且\(i+1\)没吃,概率是\((1-\frac{1}{2^x})\frac{1}{2^{x-1}}\)
  • 如果\(i\)没吃并且\(i+1\)吃了,概率是\(\frac{1}{2^x}(1-\frac{1}{2^x})\)

那么记\(p_i\)表示\(i\)留到最后的概率,就有\(p_i:p_{i+1}=\frac{1}{2^x}(1-\frac{1}{2^x}):(1-\frac{1}{2^x})\frac{1}{2^{x-1}}=1:2\)
然后又因为\(\sum_{i=1}^{k-1}p_i=1\),所以有\(p_i=\frac{2^{i-1}}{2^{k}-1}\),那么我们要求的就是\(p_1=\frac{1}{2^k-1}\)

然后这样是求第\(n\)只老鼠的,改一下其他的\(a_i\)就好了。

时间复杂度:\(O(n^5)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=55,P=998244353;
ll n,fac[N],inv[N],pw[N],pr[N];
ll a[N],f[N][N][N];
ll power(ll x,ll b){
	ll ans=1;
	while(b){
		if(b&1)ans=ans*x%P;
		x=x*x%P;b>>=1;
	}
	return ans;
}
signed main()
{
	scanf("%lld",&n);
	fac[0]=inv[0]=inv[1]=pw[0]=pr[0]=1;
	for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P;
	for(ll i=1;i<N;i++)fac[i]=fac[i-1]*i%P,inv[i]=inv[i-1]*inv[i]%P;
	for(ll i=1;i<N;i++)pw[i]=pw[i-1]*inv[2]%P,pr[i]=pr[i-1]*2ll%P;
	for(ll i=0;i<n;i++)scanf("%lld",&a[i]),a[i]=a[i]*power(100,P-2)%P;
	ll sum=0;
	for(ll s=0;s<n;s++){
		//f[i][j][k]表示到s+i,j个奶酪,被吃了k个
		memset(f,0,sizeof(f));f[0][0][0]=1;
		for(ll x=1;x<=n;x++){
			for(ll i=0;i<n;i++)
				for(ll j=0;j<=i;j++)
					for(ll k=0,r=1;k<=i-j;k++,r=r*a[(s+x)%n]%P)
						(f[x][i][j]+=f[x-1][i-k][j]*r%P*inv[k]%P)%=P;
			if(x==n)continue;
			for(ll i=0;i<n;i++)
				for(ll j=i-1;j>=0;j--){
					(f[x][i][j+1]+=f[x][i][j]*(1-pw[i-j])%P)%=P;
					(f[x][i][j]=f[x][i][j]*pw[i-j]%P)%=P;
				}
		}
		ll ans=0;
		for(ll i=1;i<=n;i++)
			(ans+=f[n][n-1][n-i]*fac[n-1]%P*power(pr[i]-1,P-2)%P)%=P;
		printf("%lld ",(ans+P)%P);sum=(sum+ans)%P;
	}
	return 0;
}
posted @ 2022-04-01 16:43  QuantAsk  阅读(32)  评论(0编辑  收藏  举报