Lucas定理,是用来解决组合数取模的一类算法.由于部分较大数据使用递推无法满足时间限制,这时就需要用到Lucas定理.
Lucas定理
Lucas定理,是用来解决组合数取模的一类算法.由于部分较大数据使用递推无法满足时间限制,这时就需要用到Lucas定理.
Lucas定理的求解方式:
对于素数p,有下式:
Cmn%p=C⌊mp⌋⌊np⌋⋅Cm%pn%pmodp
由于n%p与m%p一定小于p,可以使用递推公式直接求解;而⌊np⌋与⌊mp⌋可以继续递归使用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定理.
比如我们要求:
Cmnmodp
若p不是素数,我们如何去求呢?
设:
p=pα11pα22...
求出
⎧⎪⎨⎪⎩Cmnmod pα11Cmnmod pα22......
最后用中国剩余定理合并即可。
假设我们现在要求
CmnmodPk([P∈prime])
∴ =n!m!(n−m)!modPk
我们无法求得m!和(n−m)!的逆元,理由很简单,不一定有解。
怎么办呢?发现a对p有逆元的充要条件为
gcd(a,p)=1
所以我们换一个式子:
=n!Pxm!Py(n−m)!PzPx−y−zmodPk
其中x为n!中P因子的个数(包含多少个P因子),
其余同理.
所以如果我们对于一个n,可以求出n!Px,这样就可以求逆元了.
问题即等价于求:
n!PxmodPk
我们对n!进行变形:
n!=1⋅2⋅3⋅...⋅n
左边是1∼n中所有≤n的P的倍数,
右边相反.
因为1∼n中有⌊nP⌋个P的倍数,
∴ =P⌊nP⌋(1⋅2⋅3...)(1⋅2⋅...)
=P⌊nP⌋(⌊nP⌋)!n∏i=1,i≢0(modP)i
显然后面的mod P是有循环节的.
=P⌊nP⌋(⌊nP⌋)!(Pk∏i=1,i≢0(modP)i)⌊nPk⌋(n∏i=Pk⌊nPk⌋,i≢0(modP)i)
其中
(Pk∏i=1,i≢0(modP)i)⌊nPk⌋
是循环节1\sim P1∼P中所有无PP因子数的乘积,
(n∏i=Pk⌊nPk⌋,i≢0(modP)i)
是余项的乘积.
如
22!≡(1⋅2⋅4⋅5⋅7⋅8)(10⋅11⋅13⋅14⋅16⋅17)(19⋅20⋅22)(3⋅6⋅9⋅12⋅15⋅18⋅21)(mod32)
≡(1⋅2⋅4⋅5⋅7⋅8)2(19⋅20⋅22)37(1⋅2⋅3⋅4⋅5⋅6⋅7)(mod32)
≡377!(1⋅2⋅4⋅5⋅7⋅8)2(19⋅20⋅22)(mod32)
发现正好是一样的,⌊2232⌋=2.
=P⌊nP⌋(⌊nP⌋)!(Pk∏i=1,i≢0(modP)i)⌊nPk⌋(n∏i=Pk⌊nPk⌋,i≢0(modP)i)
发现前面这个P⌊nP⌋是要除掉的.
然而(⌊nP⌋)!里可能还包含P.
所以,我们定义函数:
f(n)=n!Px
∴ f(n)=f(⌊nP⌋)(Pk∏i=1,i≢0(modP)i)⌊nPk⌋(n∏i=Pk⌊nPk⌋,i≢0(modP)i)
这样就好了,时间复杂度是O(logPn),
边界f(0)=1.
看看原式:
=n!Pxm!Py(n−m)!PzPx−y−zmodPk
=f(n)f(m)f(n−m)Px−y−zmodPk
由于f(m)一定与Pk互质,所以我们可以直接用exgcd求逆元了.
最后我们还有一个问题,Px−y−z怎么求?
其实很好求.
比如说,我们要求f(n)=n!Px中的x,
设g(n)=x(上述的x),
再看看阶乘的式子:
n!=P⌊nP⌋(⌊nP⌋)!(Pk∏i=1,i≢0(modP)i)⌊nPk⌋(n∏i=Pk⌊nPk⌋,i≢0(modP)i)
很显然我们要的就是P⌊nP⌋.
而且(⌊nP⌋)!可能还有P因子,
所以我们可以得到以下递推式:
g(n)=⌊nP⌋+g(⌊nP⌋)
时间复杂度是O(logPn),
边界为g(n)=0(n<P),
所以答案就是:
n!Pxm!Py(n−m)!PzPg(n)−g(m)−g(n−m)modPk
最后用中国剩余定理合并答案即可得到Cmn.
代码实现:
vector<int> c,pk;
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;
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 (i≠k)
但是会有模数不互质的情况,这时就需要将其扩展了.
对于多个同余方程,有以下性质:
- 新方程与原方程具有同样的形式;
- 新方程的模数,是之前两个模数的lcm;
- 可能存在无解的情况.
对于这样一组模线性同余方程:
{a≡r1(modm1)a≡r2(modm2)
其等价于:
a=k1m1+r1=k2m2+r2
移项,得:
k1m1−k2m2=r2−r1
这时,我们可以判断该方程有无解:
- 若gcd(m1,m2)|(r2−r1),方程有解;
- 否则,方程无解.
若有解,则可以通过扩欧来计算出解的联系:
设p1=m1d,p2=m2d,则有:
k1p1−k2p2=r2−r1gcd(m1,m2)
当且仅当d|(r2−r1)时有解.
不妨设使用扩欧求得的解为x1和x2,那么有:
x1p1+x2p2=1
可变形为:
{k1=r2−r1dx1k2=−r2−r1dx2
解得
x=r1+k1m1=r1+r2−r1dx1m1
那么这个x,就满足方程组{x≡r1(modm1)x≡r2(modm2).
对于上式,有一定理:
- 若有一解xt使得方程组{x≡r1(modm1)x≡r2(modm2)成立,那么该方程组的通解为:xt+k⋅lcm(m1,m2),即:
x≡xt(modlcm(m1,m2))
证明(该内容来自网络):
证明解的唯一性,常常采用这样一种手段:假设x,y都是原问题的解,然后经过一系列推理,得到x=y,于是解的唯一性就不言而喻了.我们也采用这种手段来解决唯一性问题.设上述集合里面有0≤x,y≤lcm(m1,m2)满足
{x≡a1(modm1)x≡a2(modm2) , {y≡a1(modm1)y≡a2(modm2)
不妨设x≥y,那立刻就可以发现
{(x−y)modm1=0(x−y)modm2=0⇒lcm(m1,m2) | (x−y)
其中,x,y都是小于lcm(m1,m2)的数,它们的差也必然要小于lcm(m1,m2).但(x−y)又要被lcm(m1,m2)整除,那怎么办?只有x−y=0,也就是x=y.到此为止,我们证明了:一个完全剩余系中,有且仅有一个解.
算法流程及实现
- 读入所有方程组;
- 弹出两个方程,先判断有没有解:
- 无解:报告异常;
- 有解:合并成同一个方程,然后压进方程组;
- 重复执行上述步骤(2), 直到只剩下一个方程.这个方程就是解系.
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/Lucas.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!