题面
有一场足球比赛,还有秒就要结束了,比分还是。
主队每秒进球概率为,客队每秒进球概率为,求主队获胜概率。
注意,一秒钟一个队最多进一个球,主队获胜当且仅当主队进球比客队多。
为了避免精度误差,把最后的答案化成最简分数,输出和关于的逆元的乘积即可。
根据费马小定理
和将通过一种特别的方式给出:给出,
analysis
首先要得熟悉费马小定理:
这样就可以用快速幂求逆元了
而题中那个式子其实就是在启发我们使用费马小定理来求单个数的逆元
分析题面可知,如果主队要赢,那么假设主队进i颗球,那么客队就只能进少于i颗球
那么主队在n秒内进i颗球的概率可不可以算出来呢?相当于是一共有n颗球,主队在其中选i颗球进(里皮:还可以选球进??多少钱我来一发!),n-i颗球不进,那么一共就有种可能性能够使得主队进i颗球,那么进i颗球的概率就是
同理,假设客队进j颗球,那么概率就是
如果主队要赢,那么,于是主队赢的概率就是
根据数据范围,现在的问题就是在给定的情况下求出这个式子的值
展开式子:
于是发现一个神奇的东西:
方括号里面的东西可以O(1)递推!
假设当前最外面是第i个和式,设方括号里面的为,那么
然后最外面的次数分别递增和递减
鉴于这题卡内存,我们就用一些变量来保存这些递推的值()
对于组合数,由公式得:
由于只有分子在变,我们只需要预处理出1到n的阶乘的逆元就可以O(1)算出组合数了
线性求逆元,可以先求一个1到n的逆元,然后在做一个前缀积就可以得到1到n的阶乘的逆元,也可以直接求阶乘逆元(不知为什么我第一种方法跑不过)
时间复杂度:O(n)
code
#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define ll long long
template<typename T>void read(T &x){
x=0;char r=getchar();T neg=1;
while(r>'9'||r<'0'){if(r=='-')neg=-1;r=getchar();}
while(r>='0'&&r<='9'){x=(x<<1)+(x<<3)+r-'0';r=getchar();}
x*=neg;
}
int n;
const int maxn=1e7+10;
#define mod (1000000007)
int pa,pb,qa,qb,njc;
int inv[maxn];
int ksm(int a,int x){
int stag=a;
int res=1;
while(x){
if(x&1)res=1LL*res*stag%mod;
x>>=1;
stag=1LL*stag*stag%mod;
}
return res%mod;
}
#define Qinv(x) ksm(x,mod-2)
inline int c(int a,int b){return ((1LL*njc*inv[a]%mod*inv[n-a]%mod)%mod);}
inline int XSub(int x,int y){return (mod+(x-y)%mod)%mod;}
inline int Inc(int x,int y){return (x%mod+y%mod)%mod;}
int main(){
#ifndef ONLINE_JUDGE
freopen("datain.txt","r",stdin);
#endif
read(n);
read(pa);read(pb);read(qa);read(qb);
int p=1LL*pa*Qinv(pb)%mod;
int q=1LL*qa*Qinv(qb)%mod;
clean(inv,0);inv[1]=inv[0]=1;
njc=1;
if(!p) return putchar('0'),0;
if(!q) return printf("%d",XSub(1,ksm(XSub(1,p),n))),0;//特判p=0或q=0的情况
if(!(p^1)) return printf("%d",XSub(1,ksm(q,n))),0;//特判p=1的情况
loop(i,1,n) njc=1LL*njc*i%mod;
inv[n]=Qinv(njc);
for(register int i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;//预处理n!和阶乘逆元
int p1=1;//p1=>p^n
int p2=ksm(XSub(1,p),n);//p2=>(1-p)^n
int invp2=Qinv(XSub(1,p));//invp2=>(1-p2)^-1
int q1=Qinv(q);//q1=>q^n
int q2=ksm(XSub(1,q),n+1);//q2=>(1-q)^n
int invq2=Qinv(XSub(1,q));//invq2=>(1-q2)^-1
int sum=0;//sum=>sigma....
int res=0;
loop(i,1,n){
p1=1LL*p1*p%mod;
q1=1LL*q1*q%mod;
q2=1LL*q2*invq2%mod;
p2=1LL*p2*invp2%mod;
sum=Inc(sum,1LL*q1*q2%mod*c(i-1,n)%mod);
res=Inc(res,1LL*p1*p2%mod*sum%mod*c(i,n)%mod);
}
printf("%d\n",res);
return 0;
}