卢卡斯定理学习笔记

III.Lucas(卢卡斯定理)

Lucas定理:

(nm)(nmodpmmodp)×(n/pm/p)(modp)

该式子仅适用于 p 为质数的情形。

证明:

首先,对于 i[1,p),有 (pi)0(modp),这是显然的,因为展开式内必定包含 p 这一项。

于是,就有 (x+1)pi=0p(pi)xi1+xp(modp)

我们设 n=ap+b,m=cp+d,其中 a=nmodp,c=mmodp,且都非零。则我们即要证 (nm)(ac)×(bd)(modp)

则必然有 (x+1)n(x+1)ap×(x+1)b(xp+1)a×(x+1)b(modp)

现在,展开两个式子,得到 i=0a(ai)xip×j=0b(bj)xj

我们需要 i=c,j=d 这一项的系数,即为 (ac)×(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)i=0m(ni)(modP),也即我们要求的东西。

因为 n,m 均很大,考虑直接往上套 Lucas。

f(n,m)i=0m(ni)(modP)i=0m(n/Pi/P)(nmodPimodP)(modP)(n/P0)i=0P1(nmodPi)+(n/P1)i=0P1(nmodPi)++(n/Pi/P)i=0mmodP(nmodPi)(modP)j=0i/P1(n/Pj)i=0P1(nmodPi)+(n/Pi/P)i=0mmodP(nmodPi)(modP)f(n/P,i/P1)f(nmodP,P1)+(n/Pi/P)f(nmodP,mmodP)(modP)

除了 f(n/P,i/P1) 这一项以外,其它项的二维都在 P 以内,可以直接 P2 预处理出来,而这项可以递归求解;然后那个二项式系数就直接常规Lucas处理掉即可。

时间复杂度 O(P2+log2n)

代码:

#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]古代猪文

这题乍一看好像非常不可做,你模一个大质数求组合数,本身就是极为困难的,何况这题还在指数上……

等等,指数?

我们想起了著名的扩展欧拉定理ababmodφ(p)(modp)。在指数上,是要对 φ(p),也就是 p1 取模的!

p1,就是 999911658。分解质因数,得到 2×3×4679×35617。于是我们可以分别关于每个模数求值,最后CRT并一起即可。

我们要求 d|n(nd)。我们可以直接枚举 d,然后用Lucas求组合数,复杂度 O(n3logn+n)(据说因数个数可以按照 n3 算)。

代码:

#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 @   Troverld  阅读(113)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示