题解 | #Almost Correct#
Roulette
https://ac.nowcoder.com/acm/contest/57355/J
j题的题解思路
首先要知道逆元,快速幂,费马小定理。链接都在下文中;
首先是要求得概率,输赢之后加一元,输输赢也是加一元,以此类推有非常多种组合,故我们只需要计算连输之后破产的概率,用一减去就是赢到m+n元的概率。
所以我们要计算最大连输的次数设为R,当n=1,m=1时R=0,当n=2,m=1时,R=1,当n=3,m=1时,R=2……n=x,m=y时,R=log2(x+1);
赢的概率就是1-½^R
计算得到的概率是个除法,但是答案要求对其取模,而在模运算中不能有除法,所以我们要将其转化为乘法。
转化要用到的知识是逆元。:https://zhuanlan.zhihu.com/p/378728642 看这个就行。
之后就可以把1-½^R-->1-2^R的逆元,两个是一样的。
之后在计算中要用快速幂计算2^R,再用费马小定理得到这个的逆元。
注意每一步计算出的概率都要取模,原因是取模运算会减小值,防止数据过大溢出,并且根据同余理论,ab%m==(a%m)(b%m)%m,这样步步取模不会影响结果。
PS:(要用到快速幂取:以下是快速幂取模算法的推导文章
https://blog.csdn.net/chen_zan_yu_/article/details/90522763
x & 1的意思是判断奇偶性//a=(a*a)%c就是公式中的a^2,b=b/2同理,b>>=1就等于b/=2;b<<n左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方。 P-2是费马小定理中求逆元的方法)
#include<bits/stdc++.h>
using namespace std;
#define MOD 998244353
typedef long long ll;
ll ksm(ll a, ll b)
{
ll ret = 1;
a = a % MOD;
while(b>0) {
if(b % 2 == 1)
ret = (ret * a) % MOD;
b = b/2;
a = (a * a) % MOD;
}
return ret;
}
ll niyuan(ll a){
return ksm(a, MOD-2);
}//求解逆元
int main(){
ll n,m,k,ans;
cin>>n>>m;
m+=n;
ans=1;
//cout<<(3*niyuan(8))%MOD;样例一的8的逆元是niyuan(8)根据公式a*bmod(m)=a*a^m-2(费马小定理)
while(n<m){
ll zuixiao=log2(n+1);//最小的可连输的数量
ll next=min(m,(ll)pow(2,zuixiao+1)-1);//更新n的值
ll p=(1-niyuan(ksm(2,zuixiao))+MOD)%MOD;//赢的概率,+MOD是防止负数
ans=ans*ksm(p,next-n)%MOD;//相同概率的个数
n=next;
}
cout<<ans<<"\n";
return 0;
}