2020 年百度之星·程序设计大赛 - 复赛 Battle for Wosneth(逆元、期望)

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的攻击能让对方的生命值降低140%=0.4。同理假设Bob的攻击命中率分别为50%,我们就可以认为Bob的攻击能让对方的生命值降低150%=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} xmod2%mod。逆元相关知识可参考此篇博客。

有了以上期望得推导,以及逆元得相关知识,这道题就可以迎刃而解了,嘿嘿嘿。

2020 年百度之星·程序设计大赛 - 复赛 Battle for Wosneth(逆元、期望)_第1张图片

Code

#include

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);
    }
}

你可能感兴趣的:(算法之路,算法,程序设计,c++)