【模拟赛】游戏

考虑 \(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;
}
posted @ 2021-11-10 11:09  keepcoder  阅读(46)  评论(0编辑  收藏  举报