返回上一页

Lucas定理、扩展中国剩余定理

Lucas定理



Lucas定理,是用来解决组合数取模的一类算法.由于部分较大数据使用递推无法满足时间限制,这时就需要用到Lucas定理.

Lucas定理的求解方式:

对于素数p,有下式:

Cnm%p=CnpmpCn%pm%pmodp

由于n%pm%p一定小于p,可以使用递推公式直接求解;而npmp可以继续递归使用Lucas定理求解.这时,在前面添加一个特判:当m=0时,返回1.

代码实现:

inline int lucas(int a,int b)
{
	if(!b) return 1;
	return C(a%mod,b%mod)*lucas(a/mod,b/mod)%mod;
}

扩展Lucas定理


我们知道,Lucas定理要求模数必须为素数;而当模数不是素数的情况,我们需要用到扩展Lucas定理.
比如我们要求:

Cnmmodp

若p不是素数,我们如何去求呢?
设:

p=p1α1p2α2...

求出

{Cnmmod p1α1Cnmmod p2α2......

最后用中国剩余定理合并即可。

假设我们现在要求

CnmmodPk([Pprime])

 =n!m!(nm)!modPk

我们无法求得m!(nm)!的逆元,理由很简单,不一定有解。

怎么办呢?发现ap有逆元的充要条件为

gcd(a,p)=1

所以我们换一个式子:

=n!Pxm!Py(nm)!PzPxyzmodPk

其中xn!P因子的个数(包含多少个P因子),
其余同理.

所以如果我们对于一个n,可以求出n!Px,这样就可以求逆元了.

问题即等价于求:

n!PxmodPk

我们对n!进行变形:

n!=123...n

左边是1n中所有nP的倍数,

右边相反.

因为1n中有nPP的倍数,

 =PnP(123...)(12...)

=PnP(nP)!i=1,i0(modP)ni

显然后面的mod P是有循环节的.

=PnP(nP)!(i=1,i0(modP)Pki)nPk(i=PknPk,i0(modP)ni)

其中

(i=1,i0(modP)Pki)nPk

是循环节1\sim P1∼P中所有无PP因子数的乘积,

(i=PknPk,i0(modP)ni)

是余项的乘积.

22!(124578)(101113141617)(192022)(36912151821)(mod32)

(124578)2(192022)37(1234567)(mod32)

377!(124578)2(192022)(mod32)

发现正好是一样的,2232=2.

=PnP(nP)!(i=1,i0(modP)Pki)nPk(i=PknPk,i0(modP)ni)

发现前面这个PnP是要除掉的.

然而(nP)!里可能还包含P.

所以,我们定义函数:

f(n)=n!Px

 f(n)=f(nP)(i=1,i0(modP)Pki)nPk(i=PknPk,i0(modP)ni)

这样就好了,时间复杂度是O(logPn)

边界f(0)=1.

看看原式:

=n!Pxm!Py(nm)!PzPxyzmodPk

=f(n)f(m)f(nm)PxyzmodPk

由于f(m)一定与Pk互质,所以我们可以直接用exgcd求逆元了.

最后我们还有一个问题,Pxyz怎么求?

其实很好求.

比如说,我们要求f(n)=n!Px中的x

g(n)=x(上述的x),

再看看阶乘的式子:

n!=PnP(nP)!(i=1,i0(modP)Pki)nPk(i=PknPk,i0(modP)ni)

很显然我们要的就是PnP.

而且(nP)!可能还有P因子,

所以我们可以得到以下递推式:

g(n)=nP+g(nP)

时间复杂度是O(logPn)

边界为g(n)=0(n<P)

所以答案就是:

n!Pxm!Py(nm)!PzPg(n)g(m)g(nm)modPk

最后用中国剩余定理合并答案即可得到Cnm.
代码实现:

vector<int> c/*Cₙᵐ*/,pk/*pᵏ*/;
inline int gcd(int a,int b,int &x,int &y)
{
	if(!b)
	{
		x=1;y=0;
		return a;
	}
	int ans(gcd(b,a%b,x,y)),tx(x);
	x=y;y=tx-a/b*y;
	return ans;
}
inline int f(int n,int p,int mi)
{
	if(!n) return 1;
	int xhj(1),ys(1);//循环节、余数
	for(int i(1);i<=mi;i++)
		if(i%p) xhj=xhj*i%mi;
	xhj=ksm(xhj,n/mi,mi);
	for(int i(n/mi*mi);i<=n;i++)
		if(i%p) ys=i%mi*ys%mi;
	return f(n/p,p,mi)*xhj%mi*ys%mi;
}
inline int g(int n,int mod)
{
	if(n<mod) return 0;
	return g(n/mod,mod)+n/mod;
}
inline int C(int n,int m,int mod,int mi)
{
	int x1,x2,y;gcd(f(m,mod,mi),mi,x1,y);gcd(f(n-m,mod,mi),mi,x2,y);
	return f(n,mod,mi)*((x1%mod+mod)%mod)%mod*((x2%mod+mod)%mod)%mod*ksm(mod,g(n,mod)-g(m,mod)-g(n-m,mod),mi);
}
inline int exlucas(int n,int m,int mod)
{
	int fjs(mod)/*分解数*/,ans(0);
	for(int i(2);i*i<=mod;i++)
	{
		if(!(fjs%i))
		{
			int mi=1;//计算pᵏ
			while(!(fjs%i))
			{
				mi*=i;
				fjs/=i;
			}
			pk.push_back(mi);
			c.push_back(C(n,m,i,mi));
		}
	}
	if(fjs!=1) pk.push_back(fjs),c.push_back(C(n,m,fjs,fjs));
	for(int i(0),x,y;i<pk.size();i++)
		gcd(mod/pk[i],pk[i],x,y),
		ans=(ans+c[i]*mod/pk[i]%mod*((x%mod+mod)%mod)%mod)%mod;
	return ans;
}

(以上部分摘自网络)

扩展中国剩余定理



中国剩余定理:
Ta的核心内容即是构造一个Ri,使得

{Ri%mi=1Ri%mk=0 (ik)

但是会有模数不互质的情况,这时就需要将其扩展了.
对于多个同余方程,有以下性质:

  • 新方程与原方程具有同样的形式;
  • 新方程的模数,是之前两个模数的lcm;
  • 可能存在无解的情况.

对于这样一组模线性同余方程:

{ar1(modm1)ar2(modm2)

其等价于:

a=k1m1+r1=k2m2+r2

移项,得:

k1m1k2m2=r2r1

这时,我们可以判断该方程有无解:

  • gcd(m1,m2)|(r2r1),方程有解;
  • 否则,方程无解.

若有解,则可以通过扩欧来计算出解的联系:
p1=m1dp2=m2d,则有:

k1p1k2p2=r2r1gcd(m1,m2)

当且仅当d|(r2r1)时有解.
不妨设使用扩欧求得的解为x1x2,那么有:

x1p1+x2p2=1

可变形为:

{k1=r2r1dx1k2=r2r1dx2

解得

x=r1+k1m1=r1+r2r1dx1m1

那么这个x,就满足方程组{xr1(modm1)xr2(modm2).
对于上式,有一定理:

  • 若有一解xt使得方程组{xr1(modm1)xr2(modm2)成立,那么该方程组的通解为:xt+klcm(m1,m2),即:

xxt(modlcm(m1,m2))

证明(该内容来自网络):
证明解的唯一性,常常采用这样一种手段:假设x,y都是原问题的解,然后经过一系列推理,得到x=y,于是解的唯一性就不言而喻了.我们也采用这种手段来解决唯一性问题.设上述集合里面有0x,ylcm(m1,m2)满足

{xa1(modm1)xa2(modm2) , {ya1(modm1)ya2(modm2)

不妨设xy,那立刻就可以发现

{(xy)modm1=0(xy)modm2=0lcm(m1,m2) | (xy)

其中,xy都是小于lcm(m1,m2)的数,它们的差也必然要小于lcm(m1,m2).但(xy)又要被lcm(m1,m2)整除,那怎么办?只有xy=0,也就是x=y.到此为止,我们证明了:一个完全剩余系中,有且仅有一个解.

算法流程及实现

  1. 读入所有方程组;
  2. 弹出两个方程,先判断有没有解:
    • 无解:报告异常;
    • 有解:合并成同一个方程,然后压进方程组;
  3. 重复执行上述步骤(2), 直到只剩下一个方程.这个方程就是解系.
posted @   1Liu  阅读(126)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示