题面
解析
很有意思(难)的一道期望概率$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; }