題目鏈接:https://ac.nowcoder.com/acm/contest/5758/D
用概率公式計算可知在有解的情況下:
然後求組合數取模,由於除法不能直接取模,需要求逆元,根據費馬小定理,mod是質數,x對mod的乘法逆元就是x的mod-2次冪。至於求組合數,用公式 ,由於有除法取模,這裏需要求 m! 和 (n-m)! 的逆元,實際代碼中直接打表1e5範圍內所有階乘的逆元即可。
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5,mod=1e9+7;
ll fac[N+10],inv[N+10];
ll n,m,k,ans;
ll qpow(ll a,ll b)
{
ll s=1;
while(b)
{
if(b&1)s=s*a%mod;
b/=2;
a=a*a%mod;
}
return s%mod;
}
void get_inv() // 打表階乘的逆元
{
fac[0]=1;
for(int i=1;i<=N;i++)
fac[i]=fac[i-1]*i%mod;
inv[N]=qpow(fac[N],mod-2);
for(int i=N-1;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(ll n,ll m) // 組合數C(n,m)%mod
{
if(m>n)return 0;
return (fac[n]*inv[m]%mod*inv[n-m]%mod)%mod;
}
int main()
{
ios::sync_with_stdio(false);
get_inv();
int T;
cin>>T;
while(T--)
{
cin>>n>>m>>k;
if(m+k>n||m>n)ans=0; // 無解
else
{
ll s1=C(n,k); // 分子
ll s2=0; // 分母
for(int i=m;i<=n;i++)
s2=(s2+C(n,i))%mod;
ans=s1*qpow(s2,mod-2)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
之前的寒假算法訓練營有類似的概率題,也是把求概率變成了求乘法逆元, 但是做法卻完全不同。
2020牛客寒假算法基礎集訓營2 C題 算概率
思路:
題中已給出了乘法逆元,所以這題的重心不在於求逆元。
注意到n的範圍較小(n<=2000),所以本題採用 的動態規劃算法,時間複雜度可以接受。
設 表示 道題中做對的題目數量是 個,已知第 題做對的概率爲 ,
那麼 ,
注意初始化 ;
代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2010,mod=1e9+7;
ll dp[N][N],p[N],n;
int main()
{
ios::sync_with_stdio(false);
cin>>n;
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
cin>>p[i];
ll sum=0;
for(int j=1;j<=i;j++)
{
dp[i][j]=(dp[i-1][j-1]*p[i]%mod+dp[i-1][j]*(1-p[i]+mod)%mod)%mod;
// 不是p[j]!注意是p[i]!
sum=(sum+dp[i][j])%mod;
}
dp[i][0]=(1-sum+mod)%mod; // 做了i個題,0個正確的概率
}
for(int i=0;i<=n;i++)
i==n?printf("%lld\n",dp[n][i]):printf("%lld ",dp[n][i]);
return 0;
}