题解:P3807 【模板】卢卡斯定理/Lucas 定理

· · 题解

二项式定理

n 是正整数,则有:\begin{aligned} (x+y)^n = \sum_{i=0}^n C_{n}^{i} x^i y^{n-i} \end{aligned}

用数学归纳法证明:

n=1 时原式显然成立。

假设 n=k 时原式成立,则 n=k+1 时,有:

\begin{aligned} (x+y)^{k+1} &= (x+y)(x+y)^k \\ &=(x+y) \sum_{i=0}^{k}C_{k}^{i} x^i y^{k-i} \\ &=(x+y) (C_k^0 y^k+C_k^1 x y^{k-1}+\dots+C_k^{k-1}x^{k-1}y+C_k^k x^k) \\ &=x^{k+1} + y^{k+1} + (C_k^0+C_k^1)xy^k+(C_k^1+C_k^2)x^2y^{k-1}+\dots+(C_k^{k-1}+C_k^k)x^ky \\ &= x^{k+1} + y^{k+1} + \sum_{i=1}^k (C_k^{i-1}+C_k^{i})x^i y^{k-i+1} \end{aligned}

因为

\begin{aligned} C_k^{i-1}+C_k^{i} &= \frac{k!}{(i-1)!(k-i+1)!} +\frac{k!}{i!(k-i)!} \\ &= \frac{k!\:i!\:(k-i)!+k!\:(i-1)!\:(k-i+1)!}{i! \: (i-1)! \: (k-i)! \: (k-i+1)!} \\ &= \frac{(k+1)!}{i! (k-i+1)!} \\ &= C_{k+1}^i \end{aligned}

所以

\begin{aligned} (x+y)^{k+1} &= x^{k+1} + y^{k+1} + \sum_{i=1}^k C_{k+1}^ix^i y^{k-i+1} \\ &=\sum_{i=0}^{k+1}C_{k+1}^ix^i y^{k-i+1} \end{aligned}

因此 n=k+1 时原式也成立,得证。

一个引理

p 是素数,则有:(x+y)^p \equiv x^p + y^p \pmod p

证明:由二项式定理可知:\begin{aligned} (x+y)^p = \sum_{i=0}^p C_{p}^{i} x^i y^{p-i} \end{aligned}

其中当 1 \le i < p 时:

\begin{aligned} C_p^i &= \frac{p!}{i!(p-i)!} \\ &= \frac{p\times(p-1)!}{i \times (i-1)!(p-i)!} \\ &= \frac{p}{i} C_{p-1}^{i-1} \end{aligned}

所以

\begin{aligned} C_p^i & \equiv \frac{p}{i} C_{p-1}^{i-1} \\ & \equiv \frac{p}{i} C_{p-1}^{i-1} \times i \times inv(i)\\ & \equiv p \times inv(i) C_{p-1}^{i-1} \\ & \equiv 0 \pmod p \end{aligned}

其中 inv(i) 表示 i 在模 p 意义下的乘法逆元。因为 p 是素数且 i < p,故 inv(i) 一定存在。

原式在同余式中只剩下 i=0,p 两项,得证。

Lucas 定理

p 是素数,则有:C_n^m \equiv C_{\lfloor \frac{n}{p} \rfloor}^{\lfloor \frac{m}{p} \rfloor} \times C_{n \bmod p}^{m \bmod p} \pmod p

证明:

根据带余除法,令 n=ap+b,m=cp+d,其中 0 \le b,d < p

根据二项式定理得到式子一:

(1+x) ^n \equiv \sum_{i=0}^{n} C_n^i x^i \pmod p

根据二项式定理和引理得到式子二:

\begin{aligned} (1+x) ^n &\equiv (1+x)^{ap+b} \\ &\equiv ((1+x)^p)^a\times(1+x)^b\\ &\equiv (1+x^p)^a \times(1+x)^b \\ &\equiv (\sum_{i=0}^a C_a^i x^{ip})(\sum_{j=0}^b C_b^j x^j) \pmod p \end{aligned}

由式子一可知 x^m 的系数为 C_n^m

由式子二可知 x^m=x^{cp+d} 的系数为 C_a^c C_b^d

因为 \lfloor \frac{n}{p} \rfloor = a ,\lfloor \frac{m}{p} \rfloor = c,n \bmod p =b,m \bmod p=d

所以 C_n^m \equiv C_{\lfloor \frac{n}{p} \rfloor}^{\lfloor \frac{m}{p} \rfloor} \times C_{n \bmod p}^{m \bmod p} \pmod p,证毕。

我们发现在求 C_n^m \bmod p 时不太好求,因为是两个很大的数相除,得转换成乘的形式。

因为 p 是素数,根据费马小定理:m!^{p-1} \equiv 1 \pmod p,(n-m)!^{p-1} \pmod p

那么有:

C_n^m=\frac{n!}{m!(n-m)!} \equiv n! \times m!^{p-2} \times (n-m)!^{p-2} \pmod p

但是这样求是有一点问题的,因为 m! 或者 (n-m)! 可能是 p 的倍数,就不能用费马小定理了。

其实具体实现是可以避免的,因为在求 C_{n \bmod p}^{m \bmod p} \pmod p 时费马小定理是肯定成立的,可以直接求。 C_{\lfloor \frac{n}{p} \rfloor}^{\lfloor \frac{m}{p} \rfloor} \pmod p 虽然没法直接求,但是可以利用 Lucas 定理进一步拆开,直到都能够拆成可求解的形式即可。

Code

#include <stdio.h>
#define int long long
int f[100005];
void init(int a,int p){ //求阶乘,f[i]=i!
    f[0]=f[1]=1;
    for(int i=2;i<=a;++i)
        f[i]=(f[i-1]*i)%p;
}
int pow(int a,int b,int p){  //快速幂
    a%=p;
    int res=1;
    while(b>0){
        if(b&1ll) res=(res*a)%p;
        a=(a*a)%p;
        b>>=1ll;
    }
    return res;
}
int C(int m,int n,int p){
    if(m>n) return 0;
    return (f[n]*pow(f[m],p-2,p)%p *pow(f[n-m],p-2,p))%p; //利用费马小定理求C
}
int Lucas(int m,int n,int p){
    if(!m) return 1;
    return C(m%p,n%p,p)*Lucas(m/p,n/p,p)%p;
  //m%p、n%p时可以直接求,m/p、n/p时可能不满足费马小定理则继续递归拆解
}
signed main(){
    int T; scanf("%lld",&T);
    while(T--){
        int n,m,p; scanf("%lld%lld%lld",&n,&m,&p);
        init(n+m,p);
        printf("%lld\n",Lucas(n,n+m,p));
    }
}