卢卡斯定理学习笔记

III.Lucas(卢卡斯定理)

Lucas定理:

\[\boxed{\dbinom nm\equiv\dbinom{n\bmod p}{m\bmod p}\times\dbinom{n/p}{m/p}\pmod p} \]

该式子仅适用于 \(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。

\[\begin{aligned}f(n,m)&\equiv\sum\limits_{i=0}^m\dbinom ni&\pmod P\\&\equiv\sum\limits_{i=0}^m\dbinom{n/P}{i/P}\dbinom{n\bmod P}{i\bmod P}&\pmod P\\&\equiv\dbinom{n/P}{0}\sum\limits_{i=0}^{P-1}\dbinom{n\bmod P}{i}+\dbinom{n/P}{1}\sum\limits_{i=0}^{P-1}\dbinom{n\bmod P}{i}+\dots+\dbinom{n/P}{i/P}\sum\limits_{i=0}^{m\bmod P}\dbinom{n\bmod P}{i}&\pmod P\\&\equiv\sum\limits_{j=0}^{i/P-1}\dbinom{n/P}{j}\sum\limits_{i=0}^{P-1}\dbinom{n\bmod P}{i}+\dbinom{n/P}{i/P}\sum\limits_{i=0}^{m\bmod P}\dbinom{n\bmod P}{i}&\pmod P\\&\equiv f\Big(n/P,i/P-1\Big)f\Big(n\bmod P,P-1\Big)+\dbinom{n/P}{i/P}f\Big(n\bmod P,m\bmod P\Big)&\pmod P\end{aligned} \]

除了 \(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;
}
posted @ 2021-04-06 11:02  Troverld  阅读(110)  评论(0编辑  收藏  举报