题目分析
走在去北京八十中食堂的路上,听到pyh大佬和yyj大佬讨论,说这题是普及组难度的。以下为他们的论据:
首先发现目标的血量很少,我们可以设f(i,j)表示第i个目标剩余j血量的概率,对于每一个“锁定”技能,O(血量)更新一次,是一个普及组难度的dp。复杂度是
O(n2)
O
(
n
2
)
的(此处的n是指和n一个数量级)
void boom(int x,int p1) {
int p2=(1-p1+mod)%mod;//没打中的概率
for(RI i=0;i<=m[x];++i) {
if(i) f[x][i]=1LL*p2*f[x][i]%mod;//对于已死的情况额外考虑
if(i<m[x]) f[x][i]=(f[x][i]+1LL*f[x][i+1]*p1%mod)%mod;
}
}
于是第二问就做完了……并且也知道了每一个目标存活的概率。
现在开始看第一问,设
alive(i)
a
l
i
v
e
(
i
)
表示目标i存活的概率,
die(i)
d
i
e
(
i
)
表示目标i死亡的概率。对于一个结界技能,假设
g(t,i)
g
(
t
,
i
)
表示除了目标t以外的目标,存活i个的概率。那么目标t被打中的概率为:
发现把g设得具体一点,即前t个目标,存活i个的概率。由于已知每一个目标存活和死亡的概率,所以用一个普及组难度的dp可以求出来。然后 g(k−1,i) g ( k − 1 , i ) 就是除了目标t以外存活i个的概率了。
这样一个dp是 O(n2) O ( n 2 ) 的,对于每一个指定目标求一次,复杂度 O(n3) O ( n 3 ) ,过不了。
考虑直接算出所有指定目标存活i个的概率,也就是 g(k,i) g ( k , i ) ,然后倒推。
由于:
所以:
当然了,当 die(k)=0 d i e ( k ) = 0 时, g(k−1,i)=g(k,i+1) g ( k − 1 , i ) = g ( k , i + 1 ) ,也可求出。
这样我们可以先 O(n2) O ( n 2 ) 算出所有指定目标存活i个的概率,然后对于每一个目标 O(n) O ( n ) 倒推得答案,复杂度就是 O(n2) O ( n 2 ) 的。
三个dp均为普及组难度,综上,本题是一道普及组难度的题目。
代码
#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
const int mod=998244353,N=205;
int n,Q;
int f[N][105],t[N],g1[N],g2[N],inv[N],m[N];
int ksm(int x,int y) {
int re=1;
for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
return re;
}
void boom(int x,int p1) {
int p2=(1-p1+mod)%mod;//没打中的概率//没打中的概率
for(RI i=0;i<=m[x];++i) {
if(i) f[x][i]=1LL*p2*f[x][i]%mod;//对于已死的情况额外考虑
if(i<m[x]) f[x][i]=(f[x][i]+1LL*f[x][i+1]*p1%mod)%mod;
}
}
void query() {
int k=read();
g1[0]=1;
for(RI i=1;i<=k+1;++i) g1[i]=0;
for(RI i=1;i<=k;++i) {
t[i]=read();
int p2=f[t[i]][0],p1=(1-p2+mod)%mod;
for(RI j=i;j>=0;--j)//普及难度dp求所有目标中存活j个的概率,使用了滚动数组的方法
g1[j]=((j?1LL*g1[j-1]*p1%mod:0)+1LL*g1[j]*p2%mod)%mod;
}
for(RI i=1;i<=k;++i) {
int kans=0;
if(!f[t[i]][0]) for(RI j=0;j<k;++j) g2[j]=g1[j+1];//不会死的情况
else {//倒推
int kl=ksm(f[t[i]][0],mod-2),kp=(1-f[t[i]][0]+mod)%mod;
for(RI j=0;j<k;++j)
g2[j]=(1LL*(g1[j]-(j?1LL*g2[j-1]*kp%mod:0))*kl%mod+mod)%mod;
}
for(RI j=0;j<k;++j) kans=(kans+1LL*g2[j]*inv[j+1]%mod)%mod;//统计答案
kans=1LL*kans*(1-f[t[i]][0]+mod)%mod;
printf("%d ",kans);
}
puts("");
}
int main()
{
int bj,x,u,v;
n=read();
for(RI i=1;i<=n;++i) m[i]=read(),f[i][m[i]]=1;
inv[1]=1;for(RI i=2;i<=n;++i) inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
Q=read();
while(Q--) {
bj=read();
if(bj==0) x=read(),u=read(),v=read(),boom(x,1LL*u*ksm(v,mod-2)%mod);
else query();
}
for(RI i=1;i<=n;++i) {
int ans=0;
for(RI j=1;j<=m[i];++j) ans=(ans+1LL*j*f[i][j]%mod)%mod;
printf("%d ",ans);
}
return 0;
}