Battle for Wosneth(逆元、期望)
题目链接:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=892&pid=1001
提交链接:http://acm.hdu.edu.cn/showproblem.php?pid=6838
Problem Description
你在打游戏的时候碰到了如下问题:
有两个人记作Alice和Bob,Bob的生命值为m,Alice的生命值很高,所以可以认为是无限的。两个人的攻击命中率分别为p%,q%。两个人轮流攻击对方。从Alice开始攻击,每次攻击的时候,如果Alice命中,那么能让对方的生命值减低1,同时自己的生命值能恢复1,如果Bob命中,那么能让对方的生命值减低1,注意Bob不会自己回血。
直到Bob的血量变为0,游戏结束。Alice想知道,游戏结束的时候,自己期望生命值变化是多少,对998244353取模。
注意这里的变化量不是绝对值,也就是如果50%的概率加一,50%的概率减一,那么期望的变化量就是0。
对于一个分数a/b,其中gcd(a,b)=1,那么我们认为这个分数对998244353取模的值为一个数c(0≤c<998244353)满足bc≡a(mod998244353)。
Input
第一行一个正整数T(1≤T≤ 1 0 4 10^4 104)表示数据组数。
对于每组数据,第一行三个整数m, p, q(1<= m <=
1
0
9
10^9
109, 1<=p,q<= 100)。
Output
每组测试数据,输出一个数,表示答案。
Sample Input
2
4 100 100
1 50 50
Sample Output
1
499122177
Hint
第一组数据中,每次都能命中,所以Alice能恢复 4 点生命值,减低 3 点生命值,变化量必定为 1。
第二组数据中,对应的分数为 1/2,在Alice命中Bob之前,Bob能期望命中Alice 1/2 次。
Solution
题目是要求游戏结束的时候,自己期望生命值变化是多少,对998244353取模。
Alice和Bob两个人的攻击命中率分别为p%,q%,如果命中每次攻击能让对方的生命值减低1。主要区别是:如果Alice命中,那么能让对方的生命值减低1,同时自己的生命值能恢复1,如果Bob命中,那么能让对方的生命值减低1,但是Bob不会自己回血。
因为题目是求期望,我们就可以这样假设:Alice和Bob两个人的攻击每回合都能命中,每次攻击能让对方的生命值分别减低p%、q%。由于是求期望,情况是跟“Alice和Bob两个人的攻击命中率分别为p%,q%,如果命中每次攻击能让对方的生命值减低1”等价的。
假设Alice的攻击命中率分别为40%,我们就可以认为Alice的攻击能让对方的生命值降低 1 * 40%=0.4。同理假设Bob的攻击命中率分别为50%,我们就可以认为Bob的攻击能让对方的生命值降低1 * 50%=0.5。
有了以上的等价假设,我们就来思考问题了。
- 由于游戏结束的条件是,Bob的血量变为0。Bob的血量原来是m,这也就意味着Alice对Bob一共造成了m点伤害。又因为“如果Alice命中,那么能让对方的生命值减低1,同时自己的生命值能恢复1”,所以Alice生命值回复了m点。
- 我们再来考虑Alice损失的生命值。由于Bob共收到m点伤害,而Alice每次能对Bob造成p%点伤害,所以Alice一共攻击了Bob m p % \frac{m}{p\%} p%m次。由于Alice先攻击,所以Bob少一个回合,Bob一共攻击了Alice m p % \frac{m}{p\%} p%m-1次。Bob每次能对Alice造成q%点伤害,所以Bob一共对Alice造成了 ( m p % \frac{m}{p\%} p%m-1)*q%点伤害。也就是说Alice损失了( m p % \frac{m}{p\%} p%m-1)*q%点生命值。
- 以上的第一点是Alice回复的生命值,第二点是Alice损失的生命值。两者相减就是Alice的生命值变化了(这个值可能为负数),即 m-(
m
p
%
\frac{m}{p\%}
p%m-1)*q% 。
经过以上推导,我们知道了Alice的生命值变化量是 m-( m p % \frac{m}{p\%} p%m-1)*q% ,但这还没有完,因为这个期望还要对998244353取模。对分数(小数)取模也是这题的一个测试点(涉及逆元的知识),因为998244353是一个质数,我们可以根据费马小定理得,若求x关于998244353得逆元,则逆元为 x m o d − 2 x^{mod-2} xmod−2%mod。逆元相关知识可参考此篇博客。
有了以上期望的推导,以及逆元的相关知识,这道题就可以迎刃而解了,嘿嘿嘿。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=1e5+100;
const int N=1e5+100;
const int M=1e5+100;
ll n,m,T,p,q;
template<class T> void qr(T &x) //快读
{
int f=0;
x=0;
char c=getchar();
for(; !isdigit(c); c=getchar())
f^=c=='-';
for(; isdigit(c); c=getchar())
x=(x<<3)+(x<<1)+(c^48);
x=f?(-x):x;
}
template<class T> void qw(T x) //快写
{
if(x>=10)
qw(x/10);
putchar(x%10+'0');
}
ll qp(ll x,ll n) //快速幂(取模)
{
ll k=1;
for (; n; n>>=1,x=(ll)x*x%mod)
if (n&1) k=k*x%mod;
return k;
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
#endif
qr(T);
ll inv=qp(100,mod-2); //求100关于mod的逆元
while(T--)
{
qr(m);
qr(p);
qr(q);
p=p*inv%mod; //对p%取模
q=q*inv%mod; //对q%取模
ll ans;
ans=m-(m*qp(p,mod-2)%mod-1)*q;// m-(m/p-1)*q
if(ans<0) //期望如果是负数,则需要加上mod变成整数
ans=mod+ans%mod;
printf("%lld\n",ans);
}
}