一、题目
二、解法
把原问题分成三部分解决:锁定;结界;期望生命值。
先考虑锁定怎么处理,设 d p [ i ] [ j ] dp[i][j] dp[i][j]为第 i i i个人剩 j j j点血的概率,转移就考虑当前损不损血,暴力转移即可,这一部分时间复杂度是 O ( n q ) O(nq) O(nq),可以接受。
那么期望生命值也就很好算了,概率 × \times ×贡献即可。
考虑结界怎么处理,首先我们需要知道死的概率 p p p(也就是 d p [ i ] [ 0 ] dp[i][0] dp[i][0]),我们先钦定一个人活着,然后对剩下的人跑 d p dp dp,设 f [ i ] [ j ] f[i][j] f[i][j]为前 i i i个人有 j j j个人活着的概率,转移直接暴力,跑完 d p dp dp之后,考虑有 j j j个人活着,并且当前的人也活着,再乘上 j + 1 j+1 j+1的逆元求和即可。
上面的做法时间复杂度是 O ( n 3 c ) O(n^3c) O(n3c)的,可以拿到 70 70 70分,发现上面算法低效的原因就是做了很多无用的 d p dp dp,我们可以先把所有人的 d p dp dp跑出来,因为 d p dp dp与顺序无关,每个人都可以当最后一个,可以靠虑退掉这个人 d p dp dp,这里需要分类 p = 0 p=0 p=0的情况。如果 p = 0 p=0 p=0,那么 f [ k − 1 ] [ i ] = f [ k ] [ i + 1 ] f[k-1][i]=f[k][i+1] f[k−1][i]=f[k][i+1],否则根据转移方程来推: f [ k − 1 ] [ i ] = f [ k ] [ i ] − f [ k − 1 ] [ i − 1 ] × a l i v e d e a d f[k-1][i]=\frac{f[k][i]-f[k-1][i-1]\times alive}{dead} f[k−1][i]=deadf[k][i]−f[k−1][i−1]×alive,这样就可以优化到 O ( n 2 c ) O(n^2c) O(n2c)了。
口胡可能难以理解,请看代码 q w q qwq qwq。
#include <cstdio>
#include <cstring>
#define int long long
const int MOD = 998244353;
const int M = 205;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m[M],q,dp[M][M],f[M][M],g[M],a[M],inv[M];
//dp[i][j] 第i个剩j点血的概率(g辅助dp)
//f[i][j] 前i个有j个活着的概率
int qkpow(int a,int b)
{
int r=1;
while(b>0)
{
if(b&1) r=r*a%MOD;
a=a*a%MOD;
b>>=1;
}
return r;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
m[i]=read();
dp[i][m[i]]=1;
}
q=read();
for(int i=1;i<=n;i++)
inv[i]=qkpow(i,MOD-2);
while(q--)
{
int op=read(),k=read();
if(op==0)
{
int u=read(),v=read(),p=u*qkpow(v,MOD-2)%MOD;
memcpy(g,dp[k],sizeof dp[k]);
for(int i=1;i<=m[k];i++)
dp[k][i]=0;
for(int i=1;i<=m[k];i++)
{
dp[k][i]+=g[i]*(1-p+MOD)%MOD;
dp[k][i-1]+=g[i]*p%MOD;
}
for(int i=0;i<=m[k];i++)
dp[k][i]%=MOD;
}
else
{
for(int i=1;i<=k;i++)
a[i]=read();
f[0][0]=1;
for(int j=1;j<=k;j++)
{
int p=dp[a[j]][0];
for(int l=0;l<=k;l++)
{
f[j][l]=0;
if(l>0) f[j][l]=f[j-1][l-1]*(1-p+MOD)%MOD;
f[j][l]=(f[j][l]+f[j-1][l]*p%MOD)%MOD;
}
}
for(int i=1;i<=k;i++)
{
int ans=0,d=dp[a[i]][0];
int p=(1-dp[a[i]][0]+MOD)%MOD;
if(d==0)
{
for(int j=0;j<k;j++)
f[k-1][j]=f[k][j+1];
}
else
{
d=qkpow(d,MOD-2);
f[k-1][0]=f[k][0]*d%MOD;
for(int j=1;j<k;j++)
f[k-1][j]=(f[k][j]-f[k-1][j-1]*p%MOD+MOD)%MOD*d%MOD;
}
for(int j=0;j<k;j++)
{
ans=(ans+f[k-1][j]*p%MOD*inv[j+1]%MOD)%MOD;
}
printf("%lld ",ans);
}
puts("");
}
}
for(int i=1;i<=n;i++)
{
int ans=0;
for(int j=0;j<=m[i];j++)
ans=(ans+dp[i][j]*j)%MOD;
printf("%lld ",ans);
}
}