Lucas定理 学习笔记

一、Lucas定理

考虑经典问题:求解\(\binom{n}{m}\mod p\)的值,\(p\)是质数。

这样的问题,我们一般采用预处理阶乘及阶乘逆元的方式达到\(\mathcal O(1)\)查询,但当\(n,m\)的范围比较大,比如\(1e9\)之类的,预处理阶乘显然是不可能的了,那我们有什么方法呢:

Lucas定理:

\[\binom{n}{m}\equiv \binom{\lfloor n/p\rfloor}{\lfloor m/p\rfloor}\binom{n\%p}{m\%p} \pmod p \]

有了这个定理,实现就很简单了,将\(\binom{\lfloor n/p\rfloor}{\lfloor m/p\rfloor}\)递归处理,将\(\binom{n\%p}{m\%p}\)用线性求逆元的方法完成,总复杂度是\(O(p+log_p(n))\),可以通过这道洛谷模板题,这道题数据范围不知道为什么只出到\(1e5\),预处理阶乘都可以过。。。事实上\(Lucas\)可以做到\(n,m\le 1e18\),只要\(p\)比较小且是质数就行了。

考虑定理的证明:

首先给出引理:

引理: 当\(p\)为质数时,满足

\[(a+b)^p\equiv a^p+b^p\pmod p \]

考虑\(\binom{p}{i} \mod p\),因为\(\binom{p}{i}=\frac{p!}{i!(p-i)!}\),所以只要\(i\not=p\)\(p-i\not=p\),这个式子一定\(=0\),因此只有在\(i=0\)\(p\)时原式值大于\(0\)

于是由二项式定理:

\[(a+b)^p=\sum_{i=0}^{p}\binom{p}{i}a^ib^{p-i}\equiv \binom p0 b^p+\binom ppa^p\equiv a^p+b^p\pmod p \]

开始推导,假设\(n=n_0p+n_1,m=m_0p+m_1\),那么我们需要证明得就是:

\[\binom nm \equiv \binom{n_0}{m_0}\binom{n_1}{m_1}\pmod p \]

于是有:

\[\begin{aligned} (1+x)^n&\equiv (1+x)^{nn_0p+n_1}\\ &\equiv (1+x)^{n_0p}(1+x)^{n_1}\\ &\equiv [(1+x)^p]^{n_0}(1+x)^{n_1}\\ &\equiv (1+x^p)^{n_0}(1+x)^{n_1}\\ &\equiv \sum_{i=0}^{n_0}\binom{n_0}{i}x^{pi}\sum_{j=0}^{n_1}\binom{n_1}{j}x^j\pmod p \end{aligned} \]

再回到二项式定理得到\((1+x)^n\)的另一种表达式:

\[(1+x)^n=\sum_{i=0}^{n}\binom{n}{i}x^i \]

\[\sum_{i=0}^{n}\binom{n}{i}x^i\equiv \sum_{i=0}^{n_0}\binom{n_0}{i}x^{pi}\sum_{j=0}^{n_1}\binom{n_1}{j}x^j\pmod p \]

左式的\(x^m\)次项的系数就是\(\binom{n}{m}\),而右式中\(n_1<p,m=m_0p+m_1\),因此右式中\(x^m\)只能由\(x^{m_0p}\times x^{m_1}\)得到,系数就是

\[\binom{n_0}{m_0}\binom{n_1}{m_1} \]

因此:

\[\binom{n}{m}\equiv \binom{n_0}{m_0}\binom{n_1}{m_1}\pmod p \]

\(Lucas\)定理得证。

二、例题:

  • 洛谷模板题:P3807
    不说了,直接上代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e5+10;
    int t,n,m,p,base[N],inv[N];
    inline void init(){
    	base[0]=base[1]=1;inv[0]=inv[1]=1;
    	for(int i=2;i<p;++i){
    		base[i]=1ll*base[i-1]*i%p;
    		inv[i]=1ll*(p-p/i)*inv[p%i]%p;
    	}
    	for(int i=2;i<p;++i) inv[i]=1ll*inv[i-1]*inv[i]%p;
    }
    inline int calc(int a,int b){
    	if(a<b) return 0;
    	return 1ll*base[a]*inv[b]%p*inv[a-b]%p;
    }
    inline int Lucas(int a,int b,int p){
    	if(a<b) return 0;
    	if(b==0) return 1;
    	return 1ll*Lucas(a/p,b/p,p)*calc(a%p,b%p)%p;
    }
    int main(){
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d%d",&n,&m,&p);
    		init();
    		printf("%d\n",Lucas(n+m,n,p));
    	}
    	return 0;
    }
    
  • [CTSC2017]吉夫特

    sol

posted @ 2021-02-02 20:29  cjTQX  阅读(77)  评论(0编辑  收藏  举报