Lucas定理 学习笔记
一、Lucas定理
考虑经典问题:求解\(\binom{n}{m}\mod p\)的值,\(p\)是质数。
这样的问题,我们一般采用预处理阶乘及阶乘逆元的方式达到\(\mathcal O(1)\)查询,但当\(n,m\)的范围比较大,比如\(1e9\)之类的,预处理阶乘显然是不可能的了,那我们有什么方法呢:
Lucas定理:
有了这个定理,实现就很简单了,将\(\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\),那么我们需要证明得就是:
于是有:
再回到二项式定理得到\((1+x)^n\)的另一种表达式:
故
左式的\(x^m\)次项的系数就是\(\binom{n}{m}\),而右式中\(n_1<p,m=m_0p+m_1\),因此右式中\(x^m\)只能由\(x^{m_0p}\times x^{m_1}\)得到,系数就是
因此:
\(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; }