📜  O(1)时间复杂度中的nCr%p查询(1)

📅  最后修改于: 2023-12-03 14:44:51.242000             🧑  作者: Mango

O(1)时间复杂度中的nCr%p查询

组合数公式:C(n, m) = n! / m!(n - m)!

本文介绍如何在O(1)时间复杂度内计算nCr%p,其中n和m是非负整数,p是素数,答案需要mod p得到。

Fermat's little theorem

费马小定理是指,当p是素数,a是不是p的倍数时,有a^(p-1)≡1 (mod p)。因此,我们可以将C(n, m) mod p转换为以下形式:

C(n, m) mod p = n! * (m!)^(p-2) * ((n - m)!)^(p-2) mod p

其中,(m!)^(p-2) 和 ((n - m)!)^(p-2) 可以使用费马小定理优化计算。

O(1)时间复杂度代码示例
const int MAXN = 1e6 + 5;
const int MOD = 1e9 + 7;

int fac[MAXN], ifac[MAXN];

void init() {
    fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
    for (int i = 2; i < MAXN; i++) {
        fac[i] = 1ll * fac[i - 1] * i % MOD;
        ifac[i] = 1ll * (MOD - MOD / i) * ifac[MOD % i] % MOD;
    }
    for (int i = 2; i < MAXN; i++) {
        ifac[i] = 1ll * ifac[i] * ifac[i - 1] % MOD;
        fac[i] = 1ll * fac[i] * fac[i - 1] % MOD;
    }
}

int nCr(int n, int r) {
    if (n < r) return 0;
    return 1ll * fac[n] * ifac[r] % MOD * ifac[n - r] % MOD;
}

上述代码中,fac[i]表示i的阶乘 mod p,ifac[i]表示i的逆元 mod p。在计算阶乘和逆元时,使用了费马小定理。

首先,fac[0]和ifac[0]的值都是1,因为0! = 1,0在模p意义下的逆元也是1。然后,从i = 2到i = n,循环计算fac[i]和ifac[i],即 i! mod p 和 i在模p意义下的逆元 ifac[i]。在计算 ifac[i] 时,使用了费马小定理。

最后,在nCr函数中,如果n < r,则返回0;否则,我们可以使用以下公式计算nCr:

C(n, r) = n! / r!(n - r)!

通过在公式中使用fac和ifac,我们可以在O(1)时间复杂度内计算nCr mod p的值。

总结

本文介绍了如何在O(1)时间复杂度内计算nCr%p。通过费马小定理,我们可以将nCr转换为一系列阶乘和逆元的乘积。这种技巧可以用于解决许多组合数问题,加快程序效率。