“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛——D.扔硬币【条件概率 & 乘法逆元】(附条件概率推导过程)

题目传送门


“科林明伦杯”哈尔滨理工大学第十届程序设计竞赛——D.扔硬币【条件概率 & 乘法逆元】(附条件概率推导过程)_第1张图片


题解

  • 我们要求的是一个条件概率:在 事件B(:至少m个反面) 发生的情况下,求 事件A(:恰好k个正面) 发生的概率。
  • 即求条件概率 P ( A ∣ B ) P(A|B) P(AB)由条件概率公式得: P ( A ∣ B ) = P ( A B ) P ( B ) P(A|B)=\frac{P(AB)}{P(B)} P(AB)=P(B)P(AB)
  • P ( A B ) P(AB) P(AB) 实际上就是P(A),所以所求变成了 P ( A ) P ( B ) \frac{P(A)}{P(B)} P(B)P(A)
  • P ( A ) = C n k 2 n P(A)=\frac{C_n^k}{2^n} P(A)=2nCnk
  • P ( B ) = C n i 2 n , i ∈ [ m , n ] P(B)=\frac{C_n^i}{2^n},i\in[m, n] P(B)=2nCni,i[m,n]
  • 那么最后我们得到 P ( A ∣ B ) = C n k C n i , i ∈ [ m , n ] P(A|B)=\frac{C_n^k}{C_n^i},i\in[m, n] P(AB)=CniCnk,i[m,n]
  • C n k C_n^k Cnk 我们可以直接根据公式计算: C n k = n ! k ! ( n − k ) ! C_n^k=\frac{n!}{k!(n-k)!} Cnk=k!(nk)!n!
  • 那么我们只需要预处理一下阶乘数组即可。
  • 但是这样会超时,注意数据范围 n < 1 e 5   , m < = 1000 n<1e5\ ,m<=1000 n<1e5 ,m<=1000
  • 实际上, P ( B ) P(B) P(B) 的分子 C n i , i ∈ [ m , n ] C_n^i,i\in[m, n] Cni,i[m,n] 可以转化为: 2 n − C n i , i ∈ [ 0 , m − 1 ] 2^n-C_n^i,i\in[0, m - 1] 2nCni,i[0,m1]这样就可以了
  • 注意一下乘法逆元即可。
  • 其实条件概率还有另一种计算方式:在缩小样本空间内计算: 事 件 概 率 = 随 机 事 件 样 本 空 间 事件概率=\frac{随机事件}{样本空间} =,这种方法的计算结果就是上述推导最后得到的结果。

AC-Code

#include 
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 7;
const int mod = 1e9 + 7;

ll q_pow(ll a, ll b, ll mod) { // 快速幂
    ll res = 1LL;
    while (b) {
        if (b & 1)  res = (res * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return res % mod;
}

ll inv(ll x) {
    return q_pow(x, mod - 2, mod);
}

ll factorial[MAXN] = { 1 }; // 阶乘

inline ll C(ll n, ll k) {
    return factorial[n] * inv(factorial[n - k] * factorial[k]%mod) % mod;
}

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    factorial[0] = 1;
    for (int i = 1; i <= MAXN - 7; ++i)
        factorial[i] = factorial[i - 1] * i % mod;;
    int t;  cin >> t;
    while (t--) {
        ll n, m, k;    cin >> n >> m >> k;
        if (k + m > n) {
            cout << 0 << endl;
            continue;
        }
        ll a = C(n, k), b = q_pow(2, n, mod);
        for (int i = 0; i < m; ++i) 
            b = (b - C(n, i) + mod) % mod;
        cout << a * inv(b) % mod << endl;
    }
}

你可能感兴趣的:(数论)