链接:https://ac.nowcoder.com/acm/contest/5758/D
来源:牛客网
题意:
有n枚硬币,正反概率相同,已知至少有m枚硬币是反面,问恰好有k枚硬币是正面的概率是多少。
分析:
首先很容易,当正面硬币数+反面硬币数大于总硬币,即m+k>n时,结果不存在,概率为0。
接下来考虑一下其他合法情况,先设A为至少有m枚硬币是反面,B为恰好有k枚硬币是正面,A为已知条件,所以由高中的概率可得,P(B∣A)=P(AB)/P(A),因为 P(AB)和 P(A) 分母均为n,所以可以转化为方案数。
P(AB):使A,B同时成立的条件为 C(n,k)或者 C(n,m)。
P(A) :直接求不太好弄,逆向思维转化成总方案数减去不合法的情况d,d就是有x枚硬币是反面而且x<m的所有情况,总的方案数是2^n。
所以公式为:
当然这公式涉及到两个问题
1:求组合数:传统的递推方法由于数据大可能会超时,也可能因为除法mod会超范围,所以我们可以先预处理出所有阶乘取模的余数fact[N],以及所有阶乘取模的逆元infact[N]
如果取模的数是质数,可以用费马小定理求逆元。
费马小定理:
2:分数取模:处理p/q%mod,这就是相当于p*q的逆元%mod;
代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=100010,mod=1e9+7; LL fact[N],infact[N];//阶乘,逆元 LL quickpow(LL a,LL b)//a为底数,b为指数 { LL res=1; while(b){ if(b&1) res=(res*a)%mod; a=a*a%mod; b>>=1; } return res; } LL C(LL a,LL b) { return fact[a]*quickpow(fact[b]*fact[a-b]%mod,mod-2)%mod; } int main() { int t; cin>>t; fact[0]=1; infact[0]=1; for(int i=1;i<=100005;i++){//打表 fact[i]=fact[i-1]*i%mod;//阶乘 infact[i]=quickpow(fact[i],mod-2);//逆元 } while(t--) { LL n,m,k; cin>>n>>m>>k; if(k+m>n) cout<<0<<endl; else{ LL a=C(n,k); LL b=quickpow(2,n); for(int i=0;i<m;i++) b-=C(n,i); b=((b%mod)+mod)%mod; cout<<a*quickpow(b,mod-2)%mod<<endl;//p*q的逆元%mod 分数取模 } } return 0; }