題面
有一場足球比賽,還有秒就要結束了,比分還是。
主隊每秒進球概率爲,客隊每秒進球概率爲,求主隊獲勝概率。
注意,一秒鐘一個隊最多進一個球,主隊獲勝當且僅當主隊進球比客隊多。
爲了避免精度誤差,把最後的答案化成最簡分數,輸出和關於的逆元的乘積即可。
根據費馬小定理
和將通過一種特別的方式給出:給出,
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;
}