“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛 D题 扔硬币【组合数+逆元】

题目链接:https://ac.nowcoder.com/acm/contest/5758/D

用概率公式计算可知在有解的情况下:
a n s = C ( n , k ) C ( n , m ) + C ( n , m + 1 ) + . . . + C ( n , n ) % m o d ans = { \frac{C(n,k) }{C(n,m)+C(n,m+1)+...+C(n,n)} \%mod}

然后求组合数取模,由于除法不能直接取模,需要求逆元,根据费马小定理,mod是质数,x对mod的乘法逆元就是x的mod-2次幂。至于求组合数,用公式 C ( n , m ) = n ! m ! ( n m ) ! C(n,m)= { n! \over m!*(n-m)!} ,由于有除法取模,这里需要求 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),所以本题采用 O ( n 2 ) O(n^2) 的动态规划算法,时间复杂度可以接受。
d p [ i ] [ j ] dp[i][j] 表示 i i 道题中做对的题目数量是 j j 个,已知第 i i 题做对的概率为 p [ i ] p[i]
那么 d p [ i ] [ j ] = d p [ i 1 ] [ j 1 ] p [ i ] + d p [ i 1 ] [ j ] ( 1 p [ i ] ) dp[i][j]=dp[i-1][j-1]*p[i]+dp[i-1][j]*(1-p[i])
注意初始化 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1

代码:

#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;
}

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/106459203