题目大意:
有$n(n\le200)$个人,每个人初始血量为$m_i(m_i\le100)$对这些人进行$q(q\le2\times10^5)$次操作,操作包含以下两种:
1. 选择编号为$id$的人,有$p$的概率扣掉一滴血;
2. 编号为$id_1,id_2,\ldots,id_k$的$k$个人,等概率的从这$k$个人中选取一个活着的人封印,问这$k$个人中每个人被封印的概率是多少。
处理完所有操作后,求每个人最终血量的期望。
思路:
$f[i][j]$表示第$i$个人血量为$j$的概率,则对于每次的操作1,有转移方程$f'[i][j]=f[i][j]\times(1-p)+f[i][j+1]\times p$。特别地,对于$j=0$的情况,$f'[i][0]=f[i][0]+f[i][1]\times p$,即,血量为$0$时,不管怎么扣血,血量仍旧为$0$。
考虑操作$2$,用$alive(x)$和$dead(x)$分别表示$x$在当前局面下,存活或死亡的概率。用$g[j]$表示当前集合中不包括当前封印的有$j$人存活的概率,每次把新人$x$加入到集合中时,转移方程为$g'[j]=alive(x)\times g[j-1]+dead(x)\times g[j]$。若封印的人为$i$,则答案为$alive(i)\times\sum_{j=0}^{k-1}\frac{g[j]}{j+1}$。每次$O(k)$枚举封印的人,剩下$O(k^2)$DP,则对于每次操作2,时间复杂度为$O(k^3)$,有70分。
发现上述状态转移可逆,即$g[j]=\frac{g'[j]-alive(x)\times g[j-1]}{dead(x)}$,因此可以不考虑封印的人求出$g$数组,再枚举封印的人进行逆转移,单次操作时间复杂度$O(k^2)$。
对于期望血量,直接利用$f$数组求得。
此外本题有些卡常,需要开O2。
1 #pragma GCC optimize("Ofast")
2 #include<cstdio>
3 #include<cctype>
4 #include<algorithm>
5 typedef long long int64;
6 inline int getint() {
7 register char ch;
8 while(!isdigit(ch=getchar()));
9 register int x=ch^'0';
10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
11 return x;
12 }
13 const int N=201,M=101,mod=998244353;
14 int m[N],f[N][M],g[N],h[N],id[N],map[N];
15 void exgcd(const int &a,const int &b,int &x,int &y) {
16 if(!b) {
17 x=1,y=0;
18 return;
19 }
20 exgcd(b,a%b,y,x);
21 y-=a/b*x;
22 }
23 inline int inv(const int &x) {
24 if(x<N&&map[x]) return map[x];
25 int ret,tmp;
26 exgcd(x,mod,ret,tmp);
27 const int ans=(ret%mod+mod)%mod;
28 if(x<N) map[x]=ans;
29 return ans;
30 }
31 inline int alive(const int &x) {
32 return mod-f[x][0]+1;
33 }
34 inline int dead(const int &x) {
35 return f[x][0];
36 }
37 int main() {
38 const int n=getint();
39 for(register int i=1;i<=n;i++) {
40 f[i][m[i]=getint()]=1;
41 }
42 const int q=getint();
43 for(register int i=0;i<q;i++) {
44 const int opt=getint();
45 if(opt==0) {
46 const int id=getint(),u=getint(),v=getint(),p=(int64)u*inv(v)%mod;
47 (f[id][0]+=(int64)f[id][1]*p%mod)%=mod;
48 for(register int i=1;i<=m[id];i++) {
49 f[id][i]=(int64)f[id][i]*(mod-p+1)%mod;
50 if(i!=m[id]) (f[id][i]+=(int64)f[id][i+1]*p%mod)%=mod;
51 }
52 }
53 if(opt==1) {
54 const int k=getint();
55 std::fill(&g[g[0]=1],&g[k]+1,0);
56 for(register int i=1;i<=k;i++) {
57 id[i]=getint();
58 for(register int j=i;~j;j--) {
59 g[j]=(int64)dead(id[i])*g[j]%mod;
60 if(j>0) (g[j]+=(int64)alive(id[i])*g[j-1]%mod)%=mod;
61 }
62 }
63 for(register int i=1;i<=k;i++) {
64 int ans=0;
65 if(!dead(id[i])) {
66 for(register int j=0;j<k;j++) {
67 (ans+=(int64)g[j+1]*inv(j+1)%mod)%=mod;
68 }
69 } else {
70 std::fill(&h[h[0]=1],&h[k],0);
71 for(register int j=1;j<=k;j++) {
72 if(i!=j) h[0]=(int64)h[0]*dead(id[j])%mod;
73 }
74 for(register int j=0;j<k;j++) {
75 if(j>0) h[j]=(int64)((g[j]-(int64)alive(id[i])*h[j-1]%mod+mod)%mod)*inv(dead(id[i]))%mod;
76 (ans+=(int64)h[j]*inv(j+1)%mod)%=mod;
77 }
78 }
79 ans=(int64)ans*alive(id[i])%mod;
80 printf("%d%c",ans," \n"[i==k]);
81 }
82 }
83 }
84 for(register int i=1;i<=n;i++) {
85 int ans=0;
86 for(register int j=1;j<=m[i];j++) {
87 (ans+=(int64)j*f[i][j]%mod)%=mod;
88 }
89 printf("%d%c",ans," \n"[i==n]);
90 }
91 return 0;
92 }
来源:oschina
链接:https://my.oschina.net/u/4360327/blog/3976502