Lucas定理、扩展中国剩余定理
Lucas定理
Lucas定理,是用来解决组合数取模的一类算法.由于部分较大数据使用递推无法满足时间限制,这时就需要用到Lucas定理.
Lucas定理的求解方式:
对于素数p,有下式:
由于\(n\% p\)与\(m\% p\)一定小于\(p\),可以使用递推公式直接求解;而\({\left\lfloor \frac{n}{p} \right\rfloor}\)与\({\left\lfloor \frac{m}{p}\right\rfloor}\)可以继续递归使用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定理.
比如我们要求:
若p不是素数,我们如何去求呢?
设:
求出
最后用中国剩余定理合并即可。
假设我们现在要求
我们无法求得\(m!\)和\((n-m)!\)的逆元,理由很简单,不一定有解。
怎么办呢?发现\(a\)对\(p\)有逆元的充要条件为
所以我们换一个式子:
其中\(x\)为\(n!\)中\(P\)因子的个数(包含多少个\(P\)因子),
其余同理.
所以如果我们对于一个\(n\),可以求出\(\frac {n!}{P^x}\),这样就可以求逆元了.
问题即等价于求:
我们对\(n!\)进行变形:
左边是\(1\sim n\)中所有\(\leq n\)的\(P\)的倍数,
右边相反.
因为\(1\sim n\)中有\(\lfloor \frac nP\rfloor\)个\(P\)的倍数,
显然后面的$$\bmod\ P$$是有循环节的.
其中
是循环节1\sim P1∼P中所有无PP因子数的乘积,
是余项的乘积.
如
发现正好是一样的,\(\lfloor \frac {22}{3^2}\rfloor=2\).
发现前面这个\(P^{\lfloor \frac nP\rfloor}\)是要除掉的.
然而\((\lfloor \frac nP\rfloor)!\)里可能还包含\(P\).
所以,我们定义函数:
\(f(n)=\frac {n!}{P^x}\)
这样就好了,时间复杂度是\(O(\log_{P}n)\),
边界\(f(0)=1\).
看看原式:
由于\(f(m)\)一定与\(P^k\)互质,所以我们可以直接用\(\texttt{exgcd}\)求逆元了.
最后我们还有一个问题,\(P^{x-y-z}\)怎么求?
其实很好求.
比如说,我们要求\(f(n)=\frac {n!}{P^x}\)中的\(x\),
设\(g(n)=x\)(上述的\(x\)),
再看看阶乘的式子:
很显然我们要的就是\(P^{\lfloor \frac nP\rfloor}\).
而且\((\lfloor \frac nP\rfloor)!\)可能还有\(P\)因子,
所以我们可以得到以下递推式:
时间复杂度是\(O(\log_{P}n)\),
边界为\(g(n)=0(n<P)\),
所以答案就是:
最后用中国剩余定理合并答案即可得到\(C_n^m\).
代码实现:
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的核心内容即是构造一个\(R_i\),使得
但是会有模数不互质的情况,这时就需要将其\(\texttt{扩展}\)了.
对于多个同余方程,有以下性质:
- 新方程与原方程具有同样的形式;
- 新方程的模数,是之前两个模数的lcm;
- 可能存在无解的情况.
对于这样一组模线性同余方程:
其等价于:
移项,得:
这时,我们可以判断该方程有无解:
- 若\(\gcd(m_1, m_2) | (r_2-r_1)\),方程有解;
- 否则,方程无解.
若有解,则可以通过扩欧来计算出解的联系:
设\(p_1=\frac{m_1}{d}\),\(p_2=\frac{m_2}{d}\),则有:
当且仅当\(d | (r_2-r_1)\)时有解.
不妨设使用扩欧求得的解为\(x_1\)和\(x_2\),那么有:
可变形为:
解得
那么这个\(x\),就满足方程组\(\begin{cases}x \equiv r_1 \pmod {m_1} \\ x \equiv r_2 \pmod {m_2}\end{cases}\).
对于上式,有一定理:
- 若有一解\(x_t\)使得方程组\(\begin{cases}x \equiv r_1 \pmod {m_1} \\ x \equiv r_2 \pmod {m_2}\end{cases}\)成立,那么该方程组的通解为:\(x _ t + k\cdot \text{lcm}(m_1, m_2)\),即:
证明(该内容来自网络):
证明解的唯一性,常常采用这样一种手段:假设\(x,y\)都是原问题的解,然后经过一系列推理,得到\(x=y\),于是解的唯一性就不言而喻了.我们也采用这种手段来解决唯一性问题.设上述集合里面有\(0\leq x, y \leq \text{lcm}(m_1, m_2)\)满足
不妨设\(x\geq y\),那立刻就可以发现
其中,\(x\),\(y\)都是小于\(\text{lcm}(m_1, m_2)\)的数,它们的差也必然要小于\(\text{lcm}(m_1, m_2)\).但\((x-y)\)又要被\(\text{lcm}(m_1, m_2)\)整除,那怎么办?只有\(x-y=0\),也就是\(x=y\).到此为止,我们证明了:一个完全剩余系中,有且仅有一个解.
算法流程及实现
- 读入所有方程组;
- 弹出两个方程,先判断有没有解:
- 无解:报告异常;
- 有解:合并成同一个方程,然后压进方程组;
- 重复执行上述步骤(2), 直到只剩下一个方程.这个方程就是解系.
--END--

浙公网安备 33010602011771号
我的博客: 𝟷𝙻𝚒𝚞
本文链接: https://www.cnblogs.com/1Liu/p/Lucas.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!