【CTSC2018】假面
概率 dp
题目大意:
给定 $n$ 个怪物,第 $i$ 个怪物初始有 $m_i$ 滴血。
有两个操作:
- 给定 $k,u,v$,第 $k$ 个怪物有 $\frac u v$ 的概率掉一滴血(如果已经为 $0$ 则不管)。
- 给定 $k$ 以及 $a_1,a_2,\ldots,a_k$,你会在这 $k$ 个怪物中的活着的那些怪物里等概率选一个(都死了则忽略)。问每个怪物被选的期望。
保证 $2$ 操作个数不超过 $C$。
最后还要回答最终每个怪物血量的期望。
题解:
怪物个数和血量比较少,所以可以直接用 $g_{i,j}$ 记录第 $i$ 个怪物有 $j$ 滴血的概率。
执行操作 $1$ 的时候,有 $\frac u v$ 概率血量减少,$1-\frac u v$ 概率血量不变,直接 $O(m)$ 转移即可。
考虑操作 $2$,我们想要对每个怪物,求出其他怪物中存活 $0,1,2,\ldots,k-1$ 个的概率。
令 $f_i(x)=a_ix+(1-a_i)$,其中 $a_i$ 表示 $i$ 活着的概率。
那么我们就需要对每个 $t$ 求 $\prod_{i\neq t}f_i(x)$。
暴力求的总复杂度是 $O(n^3)$。
考虑令 $F(x)=\prod_i f_i(x)$,那么对于 $t$ 来说,我们就是要求 $\frac{F(x)}{f_t(x)}$。后者是一个 $k$ 次多项式除以一个 $1$ 次多项式,可以做到 $O(n)$。因此复杂度为 $O(n^2)$。
算血量的期望,直接枚举剩余血量,然后乘上对应概率即可。
时间复杂度 $O(Cn^2+qm)$。
Code:
#include<iostream>
#include<cstring>
typedef long long LL;
using namespace std;
const int md=998244353;
int F[202][102],n,Q,k,M[202],d[202],g[202],h[202],iv[202];
inline int pow(int a,int b){
int res=1;
for(;b;b>>=1,a=(LL)a*a%md)if(b&1)res=(LL)res*a%md;
return res;
}
void calc(){
for(int i=1;i<=k;++i){
for(int j=i;j;--j)
g[j]=((LL)g[j]*F[d[i]][0]+g[j-1]*(md+1LL-F[d[i]][0]))%md;
g[0]=(LL)g[0]*F[d[i]][0]%md;
}
}
void divide(int A,int B){
int iA=pow(A,md-2);
static int f[202];
memcpy(f,g,sizeof f);
memset(h,0,sizeof h);
for(int i=k-1;~i;--i){
int x=f[i+1]*(LL)iA%md;
h[i]=x;
f[i]=(f[i]+md-(LL)x*B%md)%md;
}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i)cin>>M[i],F[i][M[i]]=1;
iv[1]=1;
for(int i=2;i<=n;++i)iv[i]=(LL)(md-md/i)*iv[md%i]%md;
for(cin>>Q;Q--;){
int op;
cin>>op;
if(op){
cin>>k;
for(int i=1;i<=k;++i)cin>>d[i];
memset(g,0,sizeof g);
*g=1;
calc();
for(int i=1;i<=k;++i){
divide((md+1-F[d[i]][0])%md,F[d[i]][0]);
int ans=0;
for(int j=0;j<k;++j)ans=(ans+(LL)iv[j+1]*h[j])%md;
ans=ans*(md+1LL-F[d[i]][0])%md;
cout<<ans<<" \n"[i==k];
}
}else{
int u,v;
cin>>k>>u>>v;
u=(LL)u*pow(v,md-2)%md,v=md+1-u;
int*f=F[k];
f[0]=(f[0]+(LL)f[1]*u)%md;
for(int i=1;i<=M[k];++i)f[i]=((LL)f[i]*v+(LL)f[i+1]*u)%md;
}
}
for(int i=1;i<=n;++i){
int ans=0;
for(int j=1;j<=M[i];++j)
ans=(ans+(LL)j*F[i][j])%md;
cout<<ans<<" \n"[i==n];
}
return 0;
}