Lucas 定理

若无特殊说明,本章涉及的变量皆为正整数.

简介 #

Lucas 定理用于求解大组合数对质数 $p$($p≤10^6$)取模:

$${n\choose m}≡{n \bmod p\choose m \bmod p}\cdot{\lfloor n/p\rfloor\choose\lfloor m/p\rfloor} \ (\bmod \ p)$$

证明 #

我不会,长大后再学习.

解法 #

对于 $n,m≤10^6$ 的组合数 ${n\choose m}$,可以直接代入 公式,并使用 乘法逆元 将除法转为乘法.

$$\binom{n}{m}=\frac{n!}{m!(n-m)!}≡n!\cdot(m!)^{-1}\cdot[(n-m)!]^{-1} \ (\bmod \ p)$$

/* fac[x]: x!
 * inv[x]: x! 关于模 p 的乘法逆元(注意有阶乘)
 * 以上需要预处理
 */
LL C(LL n, LL m) {
    if (n < m)
        return 0;
    return fac[n] * inv[m] % P * inv[n - m] % P;
}

对于更大的组合数,套用 Lucas 定理.

$${n\choose m}≡{n \bmod p\choose m \bmod p}\cdot{\lfloor n/p\rfloor\choose\lfloor m/p\rfloor} \ (\bmod \ p)$$

其中 ${n \bmod p\choose m \bmod p}$ 可以直接算,${\lfloor n/p\rfloor\choose\lfloor m/p\rfloor}$ 需要递归求解.

LL lucas(LL n, LL m) {
    if (m == 0)
        return 1;
    return C(n % P, m % P) * lucas(n / P, m / P) % P;
}

预处理 #

  • $fac[i]=i!\bmod p$,线性递推:

    $$fac[i]=fac[i-1]\cdot i\bmod p$$

  • $inv[i]=(i!)^{-1}$,参考 线性求逆元

    $$i^{-1}=(-\Big\lfloor\frac{p}{i}\Big\rfloor\cdot(p\bmod i)^{-1}) \bmod p$$

    $$inv[i]=inv[i - 1]\cdot i^{-1}\bmod p$$

void init() {
    fac[0] = fac[1] = 1;
    for (LL i = 2; i <= P; i ++)
        fac[i] = fac[i - 1] * i % P;

    inv[0] = inv[1] = 1;
    for (LL i = 2; i <= P; i ++)
        inv[i] = (P - P / i) * inv[P % i] % P;
    for (LL i = 2; i <= P; i ++)
        inv[i] = inv[i - 1] * inv[i] % P;
}

模板 #

总时间复杂度:$O(p+\log_p{n})$.

typedef long long LL;
const int N = 1e6 + 1, P = 10007;
LL fac[N], inv[N];

void init() {
    fac[0] = fac[1] = 1;
    for (LL i = 2; i <= P; i ++)
        fac[i] = fac[i - 1] * i % P;

    inv[0] = inv[1] = 1;
    for (LL i = 2; i <= P; i ++)
        inv[i] = (P - P / i) * inv[P % i] % P;
    for (LL i = 2; i <= P; i ++)
        inv[i] = inv[i - 1] * inv[i] % P;
}

LL C(LL n, LL m) {
    if (n < m)
        return 0;
    return fac[n] * inv[m] % P * inv[n - m] % P;
}

LL lucas(LL n, LL m) {
    if (m == 0)
        return 1;
    return C(n % P, m % P) * lucas(n / P, m / P) % P;
}