题目链接
题目链接(可提交)
题意:
回合制游戏,Alice和Bob两人轮流攻击。Alice先手,每次成功的攻击自己获得1分,并给Bob造成一点血量损失,攻击命中率为p%;Bob血量为m(1e9),当血量减为0时,游戏结束,每次成功的攻击扣去Alice积分1分,攻击命中率为q%。求游戏结束时Alice积分的期望,结果对998244353取模。
思路:
期望游戏完全可以利用等效思维去考虑,那么游戏即可等效为:Alice攻击力为p%,Bob攻击力为q%
不难发现,Alice自己所增加的积分一定为Bob的血量m,而至于Bob攻击所带来的积分减少,需要算出游戏回合数即可得出结果
回合数计算也很简单,无非是m/p%。而最后一回合Bob死亡无法攻击,因此Bob回合数比Alice少一个
于是最终答案即为:m-(m/p%-1)*q
一下有几个需要注意的地方:
1.千万别忘记p和q均为百分制数,所以别忘了除100
2.对于大数取模的计算,一定要注意及时取模,减法运算完成后别忘了补加模数避免负数的出现
3.对于除法,需要用逆元运算来代替,这里可以用扩展欧几里得来计算
4.最后就是关于回合数,可能会联想到取整操作而过度理解推断出(m-1)/p+1这样的公式,然而由于逆元运算下的除法操作并不会取整,所以并不需要额外的考虑。而题目中对于除法分子分母互质的描述,也并不需要模拟去实现,只是出题者对于逆元操作的提示。
最终的公式即为:m - (m*100/p - 1) *q/100 =
m - ( ( ( ( ( (m*100)%mod *ni(p))%mod -1 +mod)%mod *q)%mod *ni(100))%mod +mod)%mod
代码:
/*
Author Owen_Q
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const double eps = 1e-7;
long long gcd(long long a,long long b, long long &x,long long &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
else
{
long long r = gcd(b,a%b,y,x);
y -= x * (a / b);
return r;
}
}
long long ni(long long a,long long n)
{
long long x, y;
long long d = gcd(a, n, x, y);
long long ans;
if(d == 1)
{
x %= n;
x += n;
x %= n;
ans = (x % n);
return ans;
}
else
{
return -1;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ll m,p,q;//,x,y;
scanf("%lld%lld%lld",&m,&p,&q);
ll t = (((m*100LL)%mod)*ni(p,mod))%mod;
ll re100 = (((t-1LL+mod)%mod)*q)%mod;
ll red = 100LL;
/*ll gcdr = gcd(re100,red,x,y);
cout << re100 << endl;
while(gcdr!=1LL)
{
re100 /= gcdr;
red /= gcdr;
gcdr = gcd(re100,red,x,y);
}*/
int re = ((m-(re100%mod) * ni(red,mod))%mod+mod)%mod;
printf("%d\n",re);
}
return 0;
}
/*
优化t计算:t=(m-1)/p+1
换用double修正t(不需要)
修正t计算:t=(m*100-1)/p+1
换用全long long计算
re增加结果取模
运用自写的gcd函数
去除结尾分子分母约分过程
re增加防负机制
删去除法
re100增加防负机制
及时取模
防负之前先取模
m利用原值
简化t计算
别总想着取整,期望考虑除法后置
细节
*/