洛谷P4564/loj2552 假面 简单dp

题目分析

走在去北京八十中食堂的路上,听到pyh大佬和yyj大佬讨论,说这题是普及组难度的。以下为他们的论据:
首先发现目标的血量很少,我们可以设f(i,j)表示第i个目标剩余j血量的概率,对于每一个“锁定”技能,O(血量)更新一次,是一个普及组难度的dp。复杂度是 O ( n 2 ) 的(此处的n是指和n一个数量级)

void boom(int x,int p1) {
    int p2=(1-p1+mod)%mod;//没打中的概率
    for(RI i=0;i<=m[x];++i) {
        if(i) f[x][i]=1LL*p2*f[x][i]%mod;//对于已死的情况额外考虑
        if(i<m[x]) f[x][i]=(f[x][i]+1LL*f[x][i+1]*p1%mod)%mod;
    }
}

于是第二问就做完了……并且也知道了每一个目标存活的概率。
现在开始看第一问,设 a l i v e ( i ) 表示目标i存活的概率, d i e ( i ) 表示目标i死亡的概率。对于一个结界技能,假设 g ( t , i ) 表示除了目标t以外的目标,存活i个的概率。那么目标t被打中的概率为:

a l i v e ( t ) i = 0 k 1 g ( t , i ) i + 1

发现把g设得具体一点,即前t个目标,存活i个的概率。由于已知每一个目标存活和死亡的概率,所以用一个普及组难度的dp可以求出来。然后 g ( k 1 , i ) 就是除了目标t以外存活i个的概率了。
这样一个dp是 O ( n 2 ) 的,对于每一个指定目标求一次,复杂度 O ( n 3 ) ,过不了。
考虑直接算出所有指定目标存活i个的概率,也就是 g ( k , i ) ,然后倒推。
由于:
g ( k , i ) = g ( k 1 , i 1 ) a l i v e ( k ) + g ( k 1 , i ) d i e ( k )

所以:
g ( k 1 , i ) = g ( k , i ) g ( k 1 , i 1 ) a l i v e ( k ) d i e ( k )

当然了,当 d i e ( k ) = 0 时, g ( k 1 , i ) = g ( k , i + 1 ) ,也可求出。
这样我们可以先 O ( n 2 ) 算出所有指定目标存活i个的概率,然后对于每一个目标 O ( n ) 倒推得答案,复杂度就是 O ( n 2 ) 的。
三个dp均为普及组难度,综上,本题是一道普及组难度的题目。
这个结论一点猫病也没有

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
const int mod=998244353,N=205;
int n,Q;
int f[N][105],t[N],g1[N],g2[N],inv[N],m[N];
int ksm(int x,int y) {
    int re=1;
    for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
    return re;
}
void boom(int x,int p1) {
    int p2=(1-p1+mod)%mod;//没打中的概率//没打中的概率
    for(RI i=0;i<=m[x];++i) {
        if(i) f[x][i]=1LL*p2*f[x][i]%mod;//对于已死的情况额外考虑
        if(i<m[x]) f[x][i]=(f[x][i]+1LL*f[x][i+1]*p1%mod)%mod;
    }
}
void query() {
    int k=read();
    g1[0]=1;
    for(RI i=1;i<=k+1;++i) g1[i]=0;
    for(RI i=1;i<=k;++i) {
        t[i]=read();
        int p2=f[t[i]][0],p1=(1-p2+mod)%mod;
        for(RI j=i;j>=0;--j)//普及难度dp求所有目标中存活j个的概率,使用了滚动数组的方法
            g1[j]=((j?1LL*g1[j-1]*p1%mod:0)+1LL*g1[j]*p2%mod)%mod;
    }
    for(RI i=1;i<=k;++i) {
        int kans=0;
        if(!f[t[i]][0]) for(RI j=0;j<k;++j) g2[j]=g1[j+1];//不会死的情况
        else {//倒推
            int kl=ksm(f[t[i]][0],mod-2),kp=(1-f[t[i]][0]+mod)%mod;
            for(RI j=0;j<k;++j)
                g2[j]=(1LL*(g1[j]-(j?1LL*g2[j-1]*kp%mod:0))*kl%mod+mod)%mod;
        }
        for(RI j=0;j<k;++j) kans=(kans+1LL*g2[j]*inv[j+1]%mod)%mod;//统计答案
        kans=1LL*kans*(1-f[t[i]][0]+mod)%mod;
        printf("%d ",kans);
    }
    puts("");
}
int main()
{
    int bj,x,u,v;
    n=read();
    for(RI i=1;i<=n;++i) m[i]=read(),f[i][m[i]]=1;
    inv[1]=1;for(RI i=2;i<=n;++i) inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
    Q=read();
    while(Q--) {
        bj=read();
        if(bj==0) x=read(),u=read(),v=read(),boom(x,1LL*u*ksm(v,mod-2)%mod);
        else query();
    }
    for(RI i=1;i<=n;++i) {
        int ans=0;
        for(RI j=1;j<=m[i];++j) ans=(ans+1LL*j*f[i][j]%mod)%mod;
        printf("%d ",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/80335787