Lucas定理

前置芝士

二项式定理(

秦九韶算法(

定义

\(P\) 为素数,\(a, b \in N^*\) ,并且

\[a = a_kp^k + a_{k - 1}p^{k-1} + \dots + a_1p+ a_0 \\ b = b_kp^k + b_{k-1}p^{k-1} + \cdots + b_1p + b_0 \]

这里 \(0 \leq a_i, b_i \leq p - 1\) 都是整数,\(i = 0,1,2, \cdots, k.\) 则有

\[C_a^b \equiv C_{a_k}^{b_k} * C_{a_{k-1}}^{b_{k-1}} * \cdots * C_{a_0}^{b_0} (mod \ P) \]

证明

​ 设 \(f(x)=a_nx^n+a_{n-1}x^{n-1}+...+a_0,g(x)=b_nx^n+b_{n-1}x^{n-1}+...+b_0\) 是两个整系数多项式,如果对 \(0\leq i\leq n\) ,都有 \(a_i\equiv b_i~(mod~m)\) ,那么称 \(f(x)\)\(g(x)\) 对模 \(m\) 同余,记作 \(f(x)\equiv g(x)~(mod~m)\)

​ 由于 \(p\) 为素数,可知对 \(1\leq j\leq p-1\) ,都有 \(C_p^j=\frac pjC_{p-1}^{j-1}\equiv 0~(mod~p)\)

于是:

\[\begin{aligned}(1+x)^p&=1+C_p^1x+...+C_p^{p-1}x^{p-1}+x^p\\&\equiv 1+x^p~(mod~p)\end{aligned} \]

利用上述结果,可知:

\[\begin{aligned}(1+x)^a&=(1+x)^{a_0}((1+x)^p)^{a_1}~~\cdot~...~\cdot~~((1+x)^{p^k})^{a_k}\\&\equiv(1+x)^{a_0}(1+x^p)^{a_1}~~\cdot~...~\cdot~~(1+x^{p^k})^{a^k}~(mod~p)\end{aligned} \]

对比两边 \(x^b(1\leq b\leq a)\) 项的系(想不明白用二项式定理),可得:

\[C_a^b\equiv C_{a_k}^{b_k}\cdot C_{a_{k-1}}^{b_{k-1}}\cdot...\cdot C_{a_0}^{b_0}~(mod~p) \]

或:

\[C_a^b\equiv \prod\limits_{i=0}^kC_{a_i}^{b_i}~(mod~p) \]

证毕

作用

​ 这个定理的意义就在于把 \(a\) 或者 \(b\) 或者两者均大于 \(p\) 的组合数 \(C_a^b\) 转换为求解小于 \(p\) 的整数 \(a_k\)\(b_k\) 的组合数 \(C^{b_k}_{a_k}\) 的乘积。

而对于 \(a_0,a_1,...,a_k\) 可以通过秦九韶算法:

\[a=(((...(0*p+a_k)...)*p+a_2)*p+a_1)*p+a_0 \]

逆向得到,即

\[a_0=(a/p^0)\%p\\a_1=(a/p^1)\%p\\a_2=(a/p^2)\%p\\\vdots \\a_k=(a/p^k)\%p \]

显然,秦九韶的逆向算法也同样适用于求解 \(b\)

(现在,通过pascal打表的方法在 \(O(1)\) 的时间里求得小范围的组合数,用逆元的方法可以求取模数范围内的组合数,在加上Lucas定理,就可以求取任意范围内的组合数了。)

代码

#include<bits/stdc++.h>
#define N 100010
using namespace std;
typedef long long ll;
ll a[N];
int p;
ll pow(ll y,int z,int p){
    y%=p;ll ans=1;
    for(int i=z;i;i>>=1,y=y*y%p)if(i&1)ans=ans*y%p;
    return ans;
}
ll C(ll n,ll m){
    if(m>n)return 0;
    return ((a[n]*pow(a[m],p-2,p))%p*pow(a[n-m],p-2,p)%p);
}
ll Lucas(ll n,ll m){
    if(!m)return 1;
    return C(n%p,m%p)*Lucas(n/p,m/p)%p;
}
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int main(){
    int T=read();
    while(T--){
        int n=read(),m=read();p=read();
        a[0]=1;
        for(int i=1;i<=p;i++)a[i]=(a[i-1]*i)%p;
        cout<<Lucas(n+m,n)<<endl;
    }
}
posted @ 2020-07-25 16:57  jasony_sam  阅读(193)  评论(0编辑  收藏  举报