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