Lucas定理 学习笔记

一、Lucas定理

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

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

Lucas定理:

[inom{n}{m}equiv inom{lfloor n/p floor}{lfloor m/p floor}inom{n\%p}{m\%p} pmod p ]

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

考虑定理的证明:

首先给出引理:

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

[(a+b)^pequiv a^p+b^ppmod p ]

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

于是由二项式定理:

[(a+b)^p=sum_{i=0}^{p}inom{p}{i}a^ib^{p-i}equiv inom p0 b^p+inom ppa^pequiv a^p+b^ppmod p ]

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

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

于是有:

[egin{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}inom{n_0}{i}x^{pi}sum_{j=0}^{n_1}inom{n_1}{j}x^jpmod p end{aligned} ]

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

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

[sum_{i=0}^{n}inom{n}{i}x^iequiv sum_{i=0}^{n_0}inom{n_0}{i}x^{pi}sum_{j=0}^{n_1}inom{n_1}{j}x^jpmod p ]

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

[inom{n_0}{m_0}inom{n_1}{m_1} ]

因此:

[inom{n}{m}equiv inom{n_0}{m_0}inom{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
    ",Lucas(n+m,n,p));
    	}
    	return 0;
    }
    
  • [CTSC2017]吉夫特

    sol

原文地址:https://www.cnblogs.com/tqxboomzero/p/14359670.html