「雅礼集训 2018 Day10」足球大战

题面

有一场足球比赛,还有nn秒就要结束了,比分还是0:00:0

主队每秒进球概率为pp,客队每秒进球概率为qq,求主队获胜概率。

注意,一秒钟一个队最多进一个球,主队获胜当且仅当主队进球比客队多。

为了避免精度误差,把最后的答案化成最简分数xy\frac{x}{y},输出xxyy关于(109+7)(10^9+7)的逆元的乘积即可。

根据费马小定理

xy mod (109+7)=x×y109+5 mod (109+7)\frac{x}{y}\ mod \ (10^9+7)=x\times y^{10^9+5} \ mod \ (10^9+7)

ppqq将通过一种特别的方式给出:给出pa,pb,qa,qbpa,pb,qa,qbp=papb,q=qaqbp=\frac{pa}{pb},q=\frac{qa}{qb}

analysis


首先要得熟悉费马小定理:
p,ap(a,p)=1,apa(modp)若p为素数,a不是p的倍数或说成(a,p)=1,则a^p\equiv a\pmod p
这样就可以用快速幂求逆元了
而题中那个式子其实就是在启发我们使用费马小定理来求单个数的逆元


分析题面可知,如果主队要赢,那么假设主队进i颗球,那么客队就只能进少于i颗球

那么主队在n秒内进i颗球的概率可不可以算出来呢?相当于是一共有n颗球,主队在其中选i颗球进(里皮:还可以选球进??多少钱我来一发!),n-i颗球不进,那么一共就有CniC_{n}^{i}种可能性能够使得主队进i颗球,那么进i颗球的概率就是P=Cnipi(1p)niP=C_{n}^{i}p^i(1-p)^{n-i}

同理,假设客队进j颗球,那么概率就是P=Cnjqj(1q)njP=C_{n}^{j}q^j(1-q)^{n-j}

如果主队要赢,那么i>j,i>=j+1i>j,也就是i>=j+1,于是主队赢的概率就是

P()=i=1nP(i)×j=0i1P(j)=i=1nCnipi(1p)ni×j=0i1Cnjqj(1q)nj P_{(主队赢)}= \sum_{i=1}^{n}P_{(主队进i颗球)}\times \sum_{j=0}^{i-1}P_{(客队进j颗球)}\\=\sum_{i=1}^{n}C_{n}^{i}p^i(1-p)^{n-i}\times \sum_{j=0}^{i-1}C_{n}^{j}q^j(1-q)^{n-j}

根据数据范围,现在的问题就是在给定p,qp,q的情况下O(n)O(n)求出这个式子的值

展开式子:

P=i=1nCnipi(1p)ni×j=0i1Cnjqj(1q)nj=Cn1p1(1p)n1×[Cn0q0(1q)n]+Cn2p2(1p)n2×[Cn0q0(1q)n+Cn1q1(1q)n1]+Cn3p3(1p)n3×[Cn0q0(1q)n+Cn1q1(1q)n1+Cn2q2(1q)n2]+Cnnpn(1p)nn×[Cn0q0(1q)n+Cnn1qn1(1q)n(n1)+Cnnqn(1q)nn] \begin{aligned} P&=\sum_{i=1}^{n}C_{n}^{i}p^i(1-p)^{n-i}\times \sum_{j=0}^{i-1}C_{n}^{j}q^j(1-q)^{n-j}\\ =&C_{n}^{1}p^1(1-p)^{n-1}\times [C_{n}^{0}q^0(1-q)^{n}]+\\ &C_{n}^{2}p^2(1-p)^{n-2}\times [C_{n}^{0}q^0(1-q)^{n}+C_{n}^{1}q^1(1-q)^{n-1}]+\\ &C_{n}^{3}p^3(1-p)^{n-3}\times [C_{n}^{0}q^0(1-q)^{n}+C_{n}^{1}q^1(1-q)^{n-1}+C_{n}^{2}q^2(1-q)^{n-2}]+\\ &\dots\\ &C_{n}^{n}p^n(1-p)^{n-n}\times [C_{n}^{0}q^0(1-q)^{n}\dots +C_{n}^{n-1}q^{n-1}(1-q)^{n-(n-1)}+C_{n}^{n}q^n(1-q)^{n-n}]\\ \end{aligned}

于是发现一个神奇的东西:

方括号里面的东西可以O(1)递推!

假设当前最外面是第i个和式,设方括号里面的为SiS_i,那么

Si=Si1+Cni1qi1(1q)niS_i=S_{i-1}+C_{n}^{i-1}q^{i-1}(1-q)^{n-i}

然后最外面的p,(1p)p,(1-p)次数分别递增和递减
鉴于这题卡内存,我们就用一些变量来保存这些递推的值(Si,q,(1q),p,(1p),inv(1q),inv(1p)S_i,q,(1-q),p,(1-p),inv(1-q),inv(1-p))

对于组合数,由公式得:

Cni=n!i!(ni)!C_{n}^{i}=\frac{n!}{i!(n-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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章