加载中…

返回上一页

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

Lucas定理



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

Lucas定理的求解方式:

对于素数p,有下式:

\[\mathrm{C}_{n}^{m}\% p = \mathrm{C}_{\left\lfloor \frac{n}{p} \right\rfloor}^{\left\lfloor \frac{m}{p}\right\rfloor}\cdot\mathrm{C}_{n\% p}^{m\% p}\bmod 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定理.
比如我们要求:

\[C_n^m \bmod{p} \]

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

\[p=p_1^{\alpha_1}p_2^{\alpha_2}... \]

求出

\[\left\{\begin{aligned}C_n^m \bmod\ {p_1^{\alpha_1}} \\C_n^m \bmod\ {p_2^{\alpha_2}}\\ ......\end{aligned} \right. \]

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

假设我们现在要求

\[C_n^m\bmod{P^k}([P\in {\texttt{prime}}]) \]

\[\therefore\ =\frac {n!}{m!(n-m)!}\bmod{P^k} \]

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

怎么办呢?发现\(a\)\(p\)有逆元的充要条件为

\[\gcd(a,p)=1 \]

所以我们换一个式子:

\[=\frac {\frac {n!}{P^{x}}}{\frac {m!}{P^{y}}\frac {(n-m)!}{P^{z}}}P^{x-y-z}\bmod{P^k} \]

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

所以如果我们对于一个\(n\),可以求出\(\frac {n!}{P^x}\),这样就可以求逆元了.

问题即等价于求:

\[\frac {n!}{P^x}\bmod{P^k} \]

我们对\(n!\)进行变形:

\[n!=1\cdot 2\cdot 3\cdot ...\cdot n \]

左边是\(1\sim n\)中所有\(\leq n\)\(P\)的倍数,

右边相反.

因为\(1\sim n\)中有\(\lfloor \frac nP\rfloor\)\(P\)的倍数,

\[\therefore\ =P^{\lfloor \frac nP\rfloor}(1 \cdot 2 \cdot 3...)(1\cdot 2\cdot ...) \]

\[=P^{\lfloor \frac nP\rfloor}(\lfloor \frac nP\rfloor)!\prod_{i=1,i\not\equiv 0\pmod {P}}^ni \]

显然后面的$$\bmod\ P$$是有循环节的.

\[=P^{\lfloor \frac nP\rfloor}(\lfloor \frac nP\rfloor)!(\prod_{i=1,i\not\equiv 0\pmod {P}}^{P^k}i)^{\lfloor \frac n{P^k}\rfloor}(\prod_{i=P^k\lfloor \frac n{P^k}\rfloor,i\not\equiv 0\pmod {P}}^ni) \]

其中

\[(\prod_{i=1,i\not\equiv 0\pmod {P}}^{P^k}i)^{\lfloor \frac n{P^k}\rfloor} \]

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

\[(\prod_{i=P^k\lfloor \frac n{P^k}\rfloor,i\not\equiv 0\pmod {P}}^ni) \]

是余项的乘积.

\[22!\equiv(1\cdot 2\cdot 4\cdot 5\cdot 7\cdot 8)(10\cdot 11\cdot 13\cdot 14\cdot 16\cdot 17)(19\cdot 20\cdot 22)(3\cdot 6\cdot 9\cdot 12\cdot 15\cdot 18\cdot 21)\pmod {3^2} \]

\[\equiv(1\cdot 2\cdot 4\cdot 5\cdot 7\cdot 8)^2(19\cdot 20\cdot 22)3^7(1\cdot 2\cdot 3\cdot 4\cdot 5\cdot 6\cdot 7)\pmod {3^2} \]

\[\equiv3^77!(1\cdot 2\cdot 4\cdot 5\cdot 7\cdot 8)^2(19\cdot 20\cdot 22)\pmod {3^2} \]

发现正好是一样的,\(\lfloor \frac {22}{3^2}\rfloor=2\).

\[=P^{\lfloor \frac nP\rfloor}(\lfloor \frac nP\rfloor)!(\prod_{i=1,i\not\equiv 0\pmod {P}}^{P^k}i)^{\lfloor \frac n{P^k}\rfloor}(\prod_{i=P^k\lfloor \frac n{P^k}\rfloor,i\not\equiv 0\pmod {P}}^ni) \]

发现前面这个\(P^{\lfloor \frac nP\rfloor}\)是要除掉的.

然而\((\lfloor \frac nP\rfloor)!\)里可能还包含\(P\).

所以,我们定义函数:

\(f(n)=\frac {n!}{P^x}\)

\[\therefore\ f(n)=f(\lfloor \frac nP\rfloor)(\prod_{i=1,i\not\equiv 0\pmod {P}}^{P^k}i)^{\lfloor \frac n{P^k}\rfloor}(\prod_{i=P^k\lfloor \frac n{P^k}\rfloor,i\not\equiv 0\pmod {P}}^ni) \]

这样就好了,时间复杂度是\(O(\log_{P}n)\)

边界\(f(0)=1\).

看看原式:

\[=\frac {\frac {n!}{P^{x}}}{\frac {m!}{P^{y}}\frac {(n-m)!}{P^{z}}}P^{x-y-z}\bmod{P^k} \]

\[=\frac {f(n)}{f(m)f(n-m)}P^{x-y-z}\bmod{P^k} \]

由于\(f(m)\)一定与\(P^k\)互质,所以我们可以直接用\(\texttt{exgcd}\)求逆元了.

最后我们还有一个问题,\(P^{x-y-z}\)怎么求?

其实很好求.

比如说,我们要求\(f(n)=\frac {n!}{P^x}\)中的\(x\)

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

再看看阶乘的式子:

\[n!=P^{\lfloor \frac nP\rfloor}(\lfloor \frac nP\rfloor)!(\prod_{i=1,i\not\equiv 0\pmod {P}}^{P^k}i)^{\lfloor \frac n{P^k}\rfloor}(\prod_{i=P^k\lfloor \frac n{P^k}\rfloor,i\not\equiv 0\pmod {P}}^ni) \]

很显然我们要的就是\(P^{\lfloor \frac nP\rfloor}\).

而且\((\lfloor \frac nP\rfloor)!\)可能还有\(P\)因子,

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

\[g(n)=\lfloor \frac nP\rfloor+g(\lfloor \frac nP\rfloor) \]

时间复杂度是\(O(\log_{P}n)\)

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

所以答案就是:

\[\frac {\frac {n!}{P^{x}}}{\frac {m!}{P^{y}}\frac {(n-m)!}{P^{z}}}P^{g(n)-g(m)-g(n-m)}\bmod{P^k} \]

最后用中国剩余定理合并答案即可得到\(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\),使得

\[\begin{cases}R_i \% m_i=1 \\ R_i \% m_k = 0 ~ (i \neq k)\end{cases} \]

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

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

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

\[\begin{cases}a \equiv r_1 \pmod {m_1} \\ a \equiv r_2 \pmod {m_2}\end{cases} \]

其等价于:

\[a = k_1m_1 + r_1 = k_2m_2 + r_2 \]

移项,得:

\[k_1m_1 - k_2m_2 = r_2 - r_1 \]

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

  • \(\gcd(m_1, m_2) | (r_2-r_1)\),方程有解;
  • 否则,方程无解.

若有解,则可以通过扩欧来计算出解的联系:
\(p_1=\frac{m_1}{d}\)\(p_2=\frac{m_2}{d}\),则有:

\[k_1 p_1 - k_2p_2 = \frac{r_2 - r_1}{\text{gcd}(m_1,m_2)} \]

当且仅当\(d | (r_2-r_1)\)时有解.
不妨设使用扩欧求得的解为\(x_1\)\(x_2\),那么有:

\[x_1p_1 + x_2p_2 = 1 \]

可变形为:

\[\begin{cases}k_1 = \frac{r_2-r_1}{d}x_1 \\ k_2 = - \frac{r_2-r_1}{d}x_2\end{cases} \]

解得

\[x=r_1+k_1m_1 = r_1 + \frac{r_2-r_1}{d}x_1m_1 \]

那么这个\(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 \equiv x_t \pmod {\text{lcm}(m_1, m_2)} \]

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

\[\begin{cases}x\equiv a_1 \pmod {m_1} \\ x\equiv a_2 \pmod {m_2}\end{cases} ~ , ~ \begin{cases}y\equiv a_1 \pmod {m_1} \\ y\equiv a_2 \pmod {m_2}\end{cases} \]

不妨设\(x\geq y\),那立刻就可以发现

\[\begin{cases}(x-y) \bmod m_1 = 0 \\ (x-y) \bmod m_2 = 0\end{cases} \quad \Rightarrow \quad\text{lcm}(m_1, m_2) ~ | ~ (x-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\).到此为止,我们证明了:一个完全剩余系中,有且仅有一个解.

算法流程及实现

  1. 读入所有方程组;
  2. 弹出两个方程,先判断有没有解:
    • 无解:报告异常;
    • 有解:合并成同一个方程,然后压进方程组;
  3. 重复执行上述步骤(2), 直到只剩下一个方程.这个方程就是解系.
posted @ 2022-05-09 11:03  1Liu  阅读(147)  评论(0)    收藏  举报