zoukankan      html  css  js  c++  java
  • [CTSC2018]假面 —— DP

    题面

      LOJ#2552

    解析

       很有意思(难)的一道期望概率$DP$

      先解决问题$1$  

      把血量当作背包重量,概率当作背包权值,可以做类似于背包的转移,对于第$x$号人,设$res1$是命中的概率,$res2$是不能命中的概率, $val[x]$是初始血量

      于是有:$dp[x][j] = dp[x][j] * res2 + dp[x][j+1] * res1 (1 leqslant j < val[x])$

      特别地:$dp[x][0] = dp[x][0] + dp[x][1] * res1$ (血量为$0$时就不再扣血了)

          $dp[x][val[x]] = dp[x][val[x]] * res2$ (血量满血时不会有转移进来的状态)

      最后的对于$i$号敌人的答案就是$sum_{j = 1}^{val[i]}j * dp[i][j]$

      注意转移方程的先后顺序即可

      再解决问题$2$

      设$f[i][j]$为前$i$个人中存活了$j$个人的概率,设第$i$个人的编号为$id$,显然可以分为第$i$个人当前是否存活进行转移:$f[i][j] = f[i-1][j] * dp[id][0] + f[i-1][j-1] * (1 - dp[id][0])$

      同样可以倒序枚举$j$,于是$f$数组就消去了第一维,此时转移方程为$f[i] = f[i] * dp[id][0] + f[i-1] * (1 - dp[id][0])$

      先把最终的$f$数组求出来,显然最终$f$数组与这$k$个人的顺序无关

      设$g[j]$为除了第$k$个人外,存活了$j$个人的概率,设第$k$个人的编号为$id$,将上式移项得$g[j] = frac{f[j] - g[j-1] * (1 - dp[id][0])}{dp[id][0]}$,特别地$g[0] =frac{f[0]}{dp[id][0]}$

      可以发现当$dp[id][0] == 0$时,会出错,此时重新带入原方程,有$g[j] = f[j+1]$,其意义为,当$id$一定存活时,除了$id$外存活了$j$个人,那么最后一定存活了$j+1$个人

      我们现在是固定了第$k$个人,若把每个人都轮流放置在第$k$号位,再对每个人进行一次逆推,就可以求出每个人的$g$数组,时间复杂度为$O(n^{2})$,可以接受

      设$p$为命中第$i$个人的概率,第$i$个人的编号为$id$,显然$p = sum_{j = 0}^{k-1}frac{g[j]*(1-dp[id][0])}{j+1}$,即$p = (1-dp[id][0])sum_{j = 0}^{k-1}frac{g[j]}{j+1}$

      依次输出$p$即可

     代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 205, mod = 998244353;
    
    inline int read()
    {
        int ret, f=1;
        char c;
        while((c=getchar())&&(c<'0'||c>'9'))if(c=='-')f=-1;
        ret=c-'0';
        while((c=getchar())&&(c>='0'&&c<='9'))ret=(ret<<3)+(ret<<1)+c-'0';
        return ret*f;
    }
    
    ll qpow(ll x, int y)
    {
        ll ret = 1LL;
        while(y)
        {
            if(y&1)
                ret = ret * x % mod;
            x = x * x % mod;
            y >>= 1;
        }
        return ret;
    }
    
    int n, m, val[maxn];
    ll dp[maxn][105], f[maxn], g[maxn], inv[maxn];
    
    void init()
    {
        inv[0] = inv[1] = 1LL;
        for(int i = 2; i <= n; ++i)
            inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;
    }
    
    void work1(int x, ll de, ll al)
    {
        ll iv = qpow(al, mod - 2);
        ll res1 = de * iv % mod, res2 = (al - de) * iv % mod;
        dp[x][0] = (dp[x][0] + (dp[x][1] * res1 % mod)) % mod;
        for(int j = 1; j < val[x]; ++j)
            dp[x][j] = ((dp[x][j] * res2 % mod) + (dp[x][j+1] * res1 % mod)) % mod;
        dp[x][val[x]] = dp[x][val[x]] * res2 % mod;
    }
    
    int num[maxn];
    
    ll ded(int x)
    {
        return dp[x][0];
    }
    
    ll ali(int x)
    {
        return (1 - dp[x][0] + mod) % mod;
    }
    
    void work2(int k)
    {
        for(int i = 1; i <= k; ++i)
            num[i] = read(), f[i] = 0;
        f[0] = 1LL;    
        for(int i = 1; i <= k; ++i)
        {
            for(int j = i; j >= 1; --j)
                f[j] = ((ded(num[i]) * f[j] % mod) + (ali(num[i]) * f[j-1] % mod)) % mod;
            f[0] = f[0] * ded(num[i]) % mod; 
        }
        ll iv, p;
        for(int i = 1; i <= k; ++i)
        {
            p = 0;
            if(!ded(num[i]))
            {
                for(int j = 0; j < k; ++j)
                    p = (p + (f[j+1] * inv[j+1] % mod)) % mod;
            }
            else
            {
                iv = qpow(ded(num[i]), mod - 2);
                g[0] = f[0] * iv % mod;    
                p = g[0] * inv[1] % mod;
                for(int j = 1; j < k; ++j)
                {
                    g[j] = ((f[j] - (g[j-1] * ali(num[i]) % mod) + mod) % mod) * iv % mod; 
                    p = (p + (g[j] * inv[j+1] % mod)) % mod;
                }
            }
            p = p * ali(num[i]) % mod;
            printf("%lld ", p);
        }
        printf("
    ");
    }
    
    int main()
    {
        n = read();
        for(int i = 1; i <= n; ++i)
            val[i] = read(), dp[i][val[i]] = 1;
        m = read();
        int opt, id, k;
        ll u, v, ans;
        init();
        while(m --)
        {
            opt = read();
            if(opt == 0)
            {
                id = read(); u = read(); v = read();
                work1(id, u, v);
            }
            else
            {
                k = read();
                work2(k);
            }
        }
        for(int i = 1; i <= n; ++i)
        {
            ans = 0;
            for(int j = 1; j <= val[i]; ++j)
                ans = (ans + (j * dp[i][j] % mod)) % mod;
            printf("%lld ", ans);
        }
        printf("
    ");
        return 0;
    }
    View Code
  • 相关阅读:
    JAVA中的CAS
    深入介绍Java中的锁[原理、锁优化、CAS、AQS]
    Java并发之AQS详解
    Java线程池ThreadPoolExecutor使用和分析(一)
    LinkedBlockingQueue
    生产者消费者两种实现:wait/notifyAll和Lock/Condition
    Java并发编程:CountDownLatch、CyclicBarrier和 Semaphore
    jvm系列(1):JVM问答
    mysql不存在插入否则更新
    java.util.MissingResourceException: Can't find bundle for base name db, locale zh_CN
  • 原文地址:https://www.cnblogs.com/Joker-Yza/p/11646991.html
Copyright © 2011-2022 走看看