【模拟赛】游戏
考虑 \(DP\) 。
如果两人都miss,则没有意义,所以有:
令转移 \(A\) 的概率为:\(p_a=\frac{p(1-q)}{1-(1-p)(1-q)}\),
转移 \(B\) 的概率为:\(p_b=\frac{q(1-p)}{1-(1-p)(1-q)}\),
转移 \(C\) 的概率为:\(p_c=1-p_a-p_b\)。
则:\(dp[i][j]=p_a\times dp[i-1][j]+p_b\times dp[i][j-1]+p_c\times dp[i-1][j-1]\)。直接 \(DP\) 是 \(O(nm)\) 的。
因为转移的概率与当前血量无关,于是我们可以用一个非常经典的套路,把整个 \(DP\) 想成一个序列,因为 \(B+C=m,A+C<n\) 。于是可以直接枚举走了多少个 \(C\),再用隔板法求出 \(B\) 的位置,最后乘上 \(A\) 的位置方案。而 \(BC\) 一共为 \(m\) 个,可以直接预处理放超过 \(k\) 个 \(A\) 的方案数。
注意实现时处理到 \(Bob\) 量为1时即可,最后 \(Alice\) 一次打败 \(Bob\) 。
code
#include<bits/stdc++.h>
using namespace std;
const int N=5e6,P=998244353,inv100=828542813;
int T,n,m,fac[N+5],inv[N+5],p,q,_p,_q;
int pa[N+5],pb[N+5],pc[N+5],sum[N+5];
int KSM(int a,int b)
{
int ret=1;
while(b)
{
if(b&1) ret=1ll*ret*a%P;
a=1ll*a*a%P;b>>=1ll;
}
return ret;
}
int C(int a,int b) {return 1ll*fac[a]*inv[b]%P*inv[a-b]%P;}
int main()
{
fac[0]=inv[0]=1;for(int i=1; i<=N; i++) fac[i]=1ll*fac[i-1]*i%P;
inv[N]=KSM(fac[N],P-2);
for(int i=N-1; i>=1; i--) inv[i]=1ll*(i+1)*inv[i+1]%P;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&p,&q);
_p=100-p;_q=100-q;
p=1ll*p*inv100%P;q=1ll*q*inv100%P;
_p=1ll*_p*inv100%P;_q=1ll*_q*inv100%P;
int hv=KSM((1-(1ll*_p*_q%P)+P)%P,P-2);
pa[0]=pb[0]=pc[0]=sum[0]=1;
pa[1]=1ll*_p*q%P*hv%P;
pb[1]=1ll*p*_q%P*hv%P;
pc[1]=1ll*p*q%P*hv%P;
sum[1]=1ll*(sum[0]+1ll*C(m,1)*pa[1]%P)%P;
for(int i=2; i<=max(n,m); i++)
pa[i]=1ll*pa[i-1]*pa[1]%P,
pb[i]=1ll*pb[i-1]*pb[1]%P,
pc[i]=1ll*pc[i-1]*pc[1]%P,
sum[i]=(sum[i-1]+1ll*C(m+i-1,i)*pa[i]%P)%P;
//sum表示选择不超过i个A的方案数。
int ans=0;
for(int i=0; i<min(n,m); i++)//枚举C的个数
{
int tmp=1ll*pc[i]*pb[m-i-1]%P;
ans+=1ll*tmp*C(m-1,i)%P*sum[n-i-1]%P,ans%=P;
}
printf("%d\n",1ll*p*hv%P*ans%P);
}
return 0;
}