[CTSC2018]假面 —— DP

流过昼夜 提交于 2019-12-01 01:55:23

题面

  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("\n");
}

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("\n");
    return 0;
}
View Code

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!