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;
}