• 【BZOJ5340】假面(CTSC2018)-概率DP


    测试地址:假面
    做法:本题需要用到概率DP。
    首先,注意到血量很小,因此对于第一种操作,直接概率DP维护敌方单位在各个血量的概率即可,式子很简单相信大家都会,我就不写了,时间复杂度为O(Qm)
    接下来,对于第二种操作,令第i个单位的存活概率为pi,则有:
    ansi=j=0k11j+1g(i,j)
    其中g(i,j)为除i之外恰有j个单位存活的概率。我们怎么计算这个概率呢?注意到这些单位的安排顺序并不影响g的值,因此我们要算第i个单位时,就把这个单位放到队列的最后,然后对前面进行概率DP。令f(i,j)为前i个单位恰有j个存活的概率,则有:
    f(i,j)=pif(i1,j1)+(1pi)f(i1,j)
    显然g(i,j)=f(k1,j)。那么我们对每个单位都进行一次这样的DP,总的时间复杂度就是O(Cn3)的,可以拿到70分。
    要进一步优化,首先要发现f(k,j)无论是在哪个单位的计算过程中都是相同的,而f(k,j)一层仅由f(k1,j)一层转移而来,那么我们可以尝试按照前面的状态转移方程倒推,于是有:
    f(k1,j)=f(k,j)pkf(k1,j1)1pk
    于是对于每个单位的计算,时间复杂度就从O(n2)优化到了O(n)。然而肯定有同学注意到了,万一pk=1就不能使用上面的式子了,那要怎么办呢?注意到此时,有f(k,j)=f(k1,j1),也就是说f(k1,j)=f(k,j+1),直接计算即可。上面的所有递推式都没有写边界条件,相信大家可以自行脑补(实在不行就请看本人的代码…)。那么我们就得到了一个总时间复杂度为O(Cn2)的算法,可以通过此题。
    我傻逼的地方:有个地方忘记取模,导致计算溢出……这应该是很低级的错误了……
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=998244353;
    int n,m[210],q,pos[210];
    ll inv[210]={0},t[210][110]={0},f[210][210]={0},g[210]={0};
    
    ll power(ll a,ll b)
    {
        ll s=1,ss=a;
        while(b)
        {
            if (b&1) s=s*ss%mod;
            ss=ss*ss%mod;b>>=1;
        }
        return s;
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&m[i]);
            t[i][m[i]]=1;
            inv[i]=power(i,mod-2);
        }
    
        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            int op,id,k;
            ll u,v;
            scanf("%d",&op);
            if (!op)
            {
                scanf("%d%lld%lld",&id,&u,&v);
                ll p=u*power(v,mod-2)%mod;
                t[id][0]=(t[id][0]+p*t[id][1])%mod;
                for(int j=1;j<=m[id];j++)
                    t[id][j]=(((1ll-p)*t[id][j]+p*t[id][j+1])%mod+mod)%mod;
            }
            else
            {
                scanf("%d",&k);
                f[0][0]=1;
                for(int j=1;j<=k;j++)
                {
                    scanf("%d",&pos[j]);
                    f[j][0]=f[j-1][0]*t[pos[j]][0]%mod;
                    for(int p=1;p<=j;p++)
                        f[j][p]=((f[j-1][p]*t[pos[j]][0]+f[j-1][p-1]*(1ll-t[pos[j]][0]))%mod+mod)%mod;
                }
                for(int j=1;j<=k;j++)
                {
                    ll ans=0;
                    if (t[pos[j]][0])
                    {
                        ll nowinv=power(t[pos[j]][0],mod-2);
                        g[0]=f[k][0]*nowinv%mod;
                        for(int p=1;p<k;p++)
                            g[p]=((f[k][p]-g[p-1]*(1ll-t[pos[j]][0]))%mod+mod)%mod*nowinv%mod;
                    }
                    else
                    {
                        for(int p=0;p<k;p++)
                            g[p]=f[k][p+1];
                    }
                    for(int p=0;p<k;p++)
                        ans=(ans+inv[p+1]*g[p])%mod;
                    printf("%lld ",(ans*(1ll-t[pos[j]][0])%mod+mod)%mod);
                }
                printf("
    ");
            }
        }
    
        for(int i=1;i<=n;i++)
        {
            ll ans=0;
            for(ll j=1;j<=m[i];j++)
                ans=(ans+j*t[i][j])%mod;
            printf("%lld ",ans);
        }
    
        return 0;
    }
  • 相关阅读:
    这段时间的总结以及未来一个月的计划
    通过配置文件构建XML
    利用汇编实现表驱动
    Intel汇编语言程序设计课后习题,6.5.5
    盲目地相信网上评价未必是好事
    ObjectiveC基础语法复习笔记
    IOS6.0 学习第1篇,基础的IOs框架
    IOS6.0 学习第2篇,弹出AlertView
    Android Fragment的使用(1)
    ObjecteiveC 属性修饰符
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793364.html
Copyright © 2020-2023  润新知