牛客周赛--round 80--G题--dp+逆元
链接:https://ac.nowcoder.com/acm/contest/101196/G
来源:牛客网
来源:牛客网
题目描述
小红和小紫正在对弈。在围棋规则中,每吃掉对方的一枚棋子,就需要将这枚棋子放入棋盖中。然而,棋盖空间不大,她们任何一方吃子数量达到 x 就输了。
当然,我们不需要考虑具体的对弈局面,模型简化如下,每个回合将会依次执行以下两步:
小红有 p1 的概率吃掉对方一枚棋子;
小紫有 p2 的概率吃掉对方一枚棋子。
谁吃子数量达到 x 就输了。小红执黑先手,她想知道自己最终获胜的概率是多少?你需要将答案对 (10^9+7) 取模后输出。
当然,我们不需要考虑具体的对弈局面,模型简化如下,每个回合将会依次执行以下两步:
小红有 p1 的概率吃掉对方一枚棋子;
小紫有 p2 的概率吃掉对方一枚棋子。
谁吃子数量达到 x 就输了。小红执黑先手,她想知道自己最终获胜的概率是多少?你需要将答案对 (10^9+7) 取模后输出。
输出描述
输出概率的逆元表示
题型
动态规划(dp)+逆元
思路
先看状态转移方程:
,最后答案求dp[0][0](注:下面的分母(1-(1-p1)*(1-p2)),用(p1+p2)-p1*p2是完全一样的)
下面是推导过程
设小红为A,小紫为B,p1=a1/b1,p2=a2/b2
设dp[i][j]为A放第i个子,B放第j个子时的概率
可以发现,每一次下棋都会出现四种情况(A吃/不吃,B吃/不吃),由于A不吃且B不吃(即(1-p1)*(1-p2))的情况对于过程没有任何贡献,故无需考虑
1.A吃/B吃(p1*p2):发现其对于i的贡献是+1,对于j的贡献也是+1
2.A吃/B不吃(p1*(1-p2)):发现其对于i的贡献是+1,对于j的贡献是0
3.A不吃/B吃((1-p1)*p2):发现其对于i的贡献是0,对于j的贡献是+1
把上面三项相乘再相加,再除以三项总概率的逆元,即为dp[i][j],由此,上面的公式就推导出来了
注意点
1.注意初始化,即dp[x][i]=0,dp[i][x]=1,(i=0~x-1(不是x!))(x为输入的吃子限定数量,因为A下了x个子A就输了,B下了x个子A就赢了)
2.注意最后求的是dp[0][0],并且由于前面的p1,p2已经是逆元表示了,所以求出的dp[0][0]也已经是逆元表示了,不需要再求逆元了
3.注意取模不要漏了,以及如果有减号的地方,后面最好加一个MOD再取余,防止出现负数
代码
#include<bits/stdc++.h> #define int long long int using namespace std; const int N=1e3+200; int MOD=1e9+7; int dp[N][N]; int qpow(int a,int b,int p){ int tmp=1; a%=p; while(b){ if(b&1) tmp=tmp*a%p; a=a*a%p; b>>=1; } return tmp%p; } int inv(int a,int p){ //求a的逆元(1/a) return qpow(a,p-2,p)%MOD; } signed main(){ int n,a,b,a2,b2,x,y; scanf("%lld",&n); scanf("%lld%lld%lld%lld",&a,&b,&a2,&b2); x=a*inv(b,MOD)%MOD; y=a2*inv(b2,MOD)%MOD; for(int i=0;i<=n-1;i++){ dp[n][i]=0; dp[i][n]=1; } int xx=(1LL+MOD-x)%MOD,yy=(1LL+MOD-y)%MOD;//xx=1-x,yy=1-y; for(int i=n-1;i>=0;i--){ for(int j=n-1;j>=0;j--){ dp[i][j]=(1LL*inv((1LL-(xx*yy)%MOD+MOD)%MOD,MOD)%MOD*(x*yy%MOD*dp[i+1][j]%MOD+xx*y%MOD*dp[i][j+1]%MOD+x*y%MOD*dp[i+1][j+1]%MOD)%MOD)%MOD; } } printf("%lld\n",(dp[0][0]+MOD)%MOD); return 0; }