2552. 「CTSC2018」假面
一道“普及难度”DP题。。。然而考场上没想出来。
一堆人题解里说“只要会期望和逆元都能AC”,我ssfd
还是在看完题解之后照着题解打的
大概就是设$f[i][j]$表示第$i$个人血量为$j$的概率
然而1号操作的转移就是$f[i][j]\leftarrow f[i][j](1-p)+f[i][j+1]p$。
二号操作就比较麻烦了。
可以枚举每个人,设$g[i]$为存活$i$个人的概率(不包括枚举的人),那么转移就是$g[i]\leftarrow g[i]dead[x]+g[i-1]alive[x]$
这个人被选的概率为$alive[x]*\sum_{i=0}^{k-1}\frac{g[i]}{i+1}$
其中$alive[x]$是$x$存活的概率,$dead[x]$是$x$死亡的概率
显然$dead[x]=f[x][0],alive[x]=1-f[x][0]$
那么得到一个二号操作一次$O(n^3)$的优秀算法。
得到70分的好成绩。
然后想想优化
好像每两次产生的$g$只会有两处不同(删一个人再加一个人可以得到新的$g$)
那么可以先不枚举,直接算出总的$g$数组
再每次删一个
$g‘[i]=g[i]dead[x]+g[i-1]alive[x]$
化成
$g[i]=\frac{g‘[i]-g[i-1]*alive[x]}{dead[x]}$
然后就可以$O(n)$求出每次的$g$数组辣
完结撒花
我还是太菜了。
#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 998244353
typedef long long ll;
il int gi(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
il vd exgcd(ll a,ll b,ll&x,ll&y){
if(b==0)x=1,y=0;
else{
exgcd(b,a%b,y,x);
y-=a/b*x;
}
}
std::map<int,int>M;
il ll inv(ll a){
if(M.find(a)!=M.end())return M[a];
ll A=a,B=mod;
ll x,y;
exgcd(A,B,x,y);
x=(x%mod+mod)%mod;
M[a]=x;
return x;
}
ll f[201][102];
ll alive[201],dead[201];
ll g[201],gg[201],a[201],m[201];
int main(){
#ifndef ONLINE_JUDGE
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
#endif
int n=gi();
for(int i=1;i<=n;++i)f[i][m[i]=gi()]=1;
int Q=gi(),op,id,u,v;
for(int i=1;i<=Q;++i){
op=gi();
if(op==0){
id=gi(),u=gi(),v=gi();
int p=1ll*u*inv(v)%mod;
f[id][0]=(f[id][0]+1ll*f[id][1]*p%mod)%mod;
for(int j=1;j<=m[id];++j)f[id][j]=(f[id][j]*(mod+1-p)%mod+f[id][j+1]*p%mod)%mod;
}else{
for(int j=1;j<=n;++j)alive[j]=(mod+1-f[j][0])%mod;
for(int j=1;j<=n;++j)dead[j]=f[j][0];
int tot=gi();
for(int j=1;j<=tot;++j)a[j]=gi();
for(int j=1;j<=n;++j)g[j]=0;
g[0]=1;
for(int j=1;j<=tot;++j){
for(int k=j;k;--k)g[k]=(g[k-1]*alive[a[j]]+g[k]*dead[a[j]]%mod)%mod;
g[0]=g[0]*dead[a[j]]%mod;
}
for(int x=1;x<=tot;++x){
if(dead[a[x]]==0)for(int j=0;j<tot;++j)gg[j]=g[j+1];
else{
gg[0]=1;
for(int j=1;j<=tot;++j)if(x!=j)gg[0]=gg[0]*dead[a[j]]%mod;
for(int j=1;j<tot;++j)gg[j]=(g[j]-gg[j-1]*alive[a[x]]%mod+mod)%mod*inv(dead[a[x]])%mod;
}
ll ans=0;
for(int k=1;k<=tot;++k)ans+=gg[k-1]*inv(k)%mod;
printf("%lld ",alive[a[x]]*(ans%mod)%mod);
}
puts("");
}
}
for(int i=1;i<=n;++i){
int ans=0;
for(int j=1;j<=m[i];++j)ans=(ans+j*f[i][j]%mod)%mod;
printf("%d ",ans);
}
puts("");
return 0;
}
原文地址:https://www.cnblogs.com/ssfdJR/p/9042981.html
时间: 2024-10-07 15:12:04