卢卡斯定理学习笔记
III.Lucas(卢卡斯定理)
Lucas定理:
该式子仅适用于 \(p\) 为质数的情形。
证明:
首先,对于 \(i\in[1,p)\),有 \(\dbinom{p}{i}\equiv0\pmod p\),这是显然的,因为展开式内必定包含 \(p\) 这一项。
于是,就有 \((x+1)^p\equiv\sum\limits_{i=0}^p\dbinom pix^i\equiv 1+x^p\pmod p\)。
我们设 \(n=ap+b,m=cp+d\),其中 \(a=n\bmod p,c=m\bmod p\),且都非零。则我们即要证 \(\dbinom nm\equiv\dbinom ac\times\dbinom bd\pmod p\)。
则必然有 \((x+1)^n\equiv(x+1)^{ap}\times(x+1)^b\equiv(x^p+1)^a\times(x+1)^b\pmod p\)。
现在,展开两个式子,得到 \(\sum\limits_{i=0}^a\dbinom aix^{ip}\times\sum\limits_{j=0}^b\dbinom bjx^j\)。
我们需要 \(i=c,j=d\) 这一项的系数,即为 \(\dbinom{a}{c}\times\dbinom bd\)。
(实际上关键就在于我们最上面证出的那个式子)
III.I.【模板】卢卡斯定理
代码:
#include<bits/stdc++.h>
using namespace std;
int T,n,m,mod,fac[100100],inv[100100];
int ksm(int x,int y=mod-2){
int z=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
return z;
}
int C(int x,int y){
if(x<y)return 0;
if(x<mod&&y<mod)return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
return 1ll*C(x/mod,y/mod)*C(x%mod,y%mod)%mod;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&mod);
fac[0]=1;for(int i=1;i<mod;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv[mod-1]=ksm(fac[mod-1]);for(int i=mod-2;i>=0;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
printf("%d\n",C(n+m,n));
}
return 0;
}
III.II.[SHOI2015]超能粒子炮·改
设 \(P=2333\)。发现它是个质数。设 \(f(n,m)\equiv\sum\limits_{i=0}^m\dbinom ni\pmod P\),也即我们要求的东西。
因为 \(n,m\) 均很大,考虑直接往上套 Lucas。
除了 \(f\Big(n/P,i/P-1\Big)\) 这一项以外,其它项的二维都在 \(P\) 以内,可以直接 \(P^2\) 预处理出来,而这项可以递归求解;然后那个二项式系数就直接常规Lucas处理掉即可。
时间复杂度 \(O(P^2+\log^2n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int P=2333;
typedef long long ll;
int C[2510][2510],S[2510][2510],T;
ll n,m;
int binom(ll x,ll y){if(x<y)return 0;if(x<P&&y<P)return C[x][y];return binom(x/P,y/P)*C[x%P][y%P]%P;}
int monib(ll x,ll y){if(y<0)return 0;if(x<P)return S[x][min(y,1ll*P-1)];return (S[x%P][P-1]*monib(x/P,y/P-1)+binom(x/P,y/P)*S[x%P][y%P])%P;}
int main(){
for(int i=0;i<P;i++)C[i][0]=C[i][i]=1;
for(int i=1;i<P;i++)for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
for(int i=0;i<P;i++){S[i][0]=C[i][0];for(int j=1;j<P;j++)S[i][j]=(S[i][j-1]+C[i][j])%P;}
scanf("%d",&T);
while(T--)scanf("%lld%lld",&n,&m),printf("%d\n",monib(n,m));
return 0;
}
III.III.[SDOI2010]古代猪文
这题乍一看好像非常不可做,你模一个大质数求组合数,本身就是极为困难的,何况这题还在指数上……
等等,指数?
我们想起了著名的扩展欧拉定理:\(a^b\equiv a^{b\bmod\varphi(p)}\pmod p\)。在指数上,是要对 \(\varphi(p)\),也就是 \(p-1\) 取模的!
\(p-1\),就是 \(999911658\)。分解质因数,得到 \(2\times3\times4679\times35617\)。于是我们可以分别关于每个模数求值,最后CRT并一起即可。
我们要求 \(\sum\limits_{d|n}\dbinom nd\)。我们可以直接枚举 \(d\),然后用Lucas求组合数,复杂度 \(O(\sqrt[3]{n}\log n+\sqrt{n})\)(据说因数个数可以按照 \(\sqrt[3]{n}\) 算)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,g;
struct func{
int mod,fac[50100],inv[50100];
int ksm(int x,int y){
int z=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
return z;
}
int C(int x,int y){
if(x<y)return 0;
if(x<mod&&y<mod)return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;
return 1ll*C(x%mod,y%mod)*C(x/mod,y/mod)%mod;
}
int solve(){
fac[0]=1;for(int i=1;i<mod;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv[mod-1]=ksm(fac[mod-1],mod-2);for(int i=mod-2;i>=0;i--)inv[i]=1ll*inv[i+1]*(i+1)%mod;
int ret=0;
for(int i=1;i*i<=n;i++){
if(n%i)continue;
(ret+=C(n,i))%=mod;
if(i*i!=n)(ret+=C(n,n/i))%=mod;
}
return ret;
}
}a[4];
const int mod=999911659;
const int phi=999911658;
int res;
int ksm(int x,int y,int mod){
int z=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
return z;
}
int main(){
scanf("%d%d",&n,&g);
if(!(g%mod)){puts("0");return 0;}
a[0].mod=2,a[1].mod=3,a[2].mod=4679,a[3].mod=35617;
for(int i=0;i<4;i++)(res+=1ll*a[i].solve()*(phi/a[i].mod)%phi*ksm(phi/a[i].mod,a[i].mod-2,a[i].mod)%phi)%=phi;
printf("%d\n",ksm(g,res,mod));
return 0;
}