#2552. 「CTSC2018」假面

Posted ssfdjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#2552. 「CTSC2018」假面相关的知识,希望对你有一定的参考价值。

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

以上是关于#2552. 「CTSC2018」假面的主要内容,如果未能解决你的问题,请参考以下文章

[CTSC2018]假面

[CTSC2018] 假面

[CTSC2018]假面

bzoj 5340: [Ctsc2018]假面

并不对劲的bzoj5340: [Ctsc2018]假面

[CTSC2016]假面(概率DP)