Lucas定理入门

前置结论

如果 \(p\) 为素数,有以下结论:

  1. \(a^p \equiv a \pmod p\)
    即费马小定理

  2. \[C_{p}^i \equiv \begin{cases} 1 & i=0 或者 i=p \\ 0 & 其他情况 \end{cases} \pmod p \]

证明可以展开

  1. \((a+b)^p \equiv a^p + b^p \pmod p\)
    证明1:用结论1

    \[\begin{aligned} (a+b)^p &\equiv a+b \\ &\equiv a^p + b^p \pmod p \end{aligned} \]

    证明2:用结论2 和 二项式定理

    \[\begin{aligned} (a+b)^p &\equiv \sum_{i=0}^{p} C_{p}^{i} \times a^i \times b^{p-i} \\ &\equiv a^p + b^p \pmod p \end{aligned} \]


Lucas定理

内容

$ C_{n}^{m} \equiv C_{\lfloor \frac{n}{p} \rfloor} ^{\lfloor \frac{m}{p} \rfloor} \cdot C_{n \bmod p}^{m \bmod p} \pmod p \text{ p 是 质数}$

用途

当质数 \(p\) 较小时,求解组合数在模意义下的值


证明

二项式定理得:\(C_n^m\) 等于 \((x+1)^n\) 展开后 \(x^m\) 的系数

\[\begin{aligned} (x+1)^n &\equiv (x+1)^{\lfloor \frac{n}{p} \rfloor \times p + n \bmod p} \\&\equiv (x+1)^{\lfloor \frac{n}{p} \rfloor \times p} \cdot (x+1)^{n \bmod p} \\&\equiv (x^p + 1)^{\lfloor \frac{n}{p} \rfloor} \cdot (x+1)^{n \bmod p} \end{aligned} \]

所以 \((x+1)^n\) 展开后 \(x^m\) 的系数 = \(\sum (x^p + 1)^{\lfloor \frac{n}{p} \rfloor}\)展开后\(x^{m-r}\)的系数 \(\times (x+1)^{n \bmod p}\)展开后 \(x^r\) 的系数

容易得到 \(r \le n\bmod p < p\) 并且 \(p \mid (m-r)\) , 也就是说 \(m\) 可以写成 \(m=k \times p + r , r<p\) 的形式,根据带余除法可知这种表示是唯一的,即只有一个唯一的 \(r\)\(k\) ,其中 $r = m \bmod p,k = \lfloor \frac{m}{p} \rfloor $

所以$ C_{n}^{m} \equiv C_{\lfloor \frac{n}{p} \rfloor} ^{\lfloor \frac{m}{p} \rfloor} \cdot C_{n \bmod p}^{m \bmod p} \pmod p$


代码实现

【模板】卢卡斯定理/Lucas 定理

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=2e5+5;
inline int read(){
    int w = 1, s = 0;
    char c = getchar();
    for (; c < '0' || c > '9'; w *= (c == '-') ? -1 : 1, c = getchar());
    for (; c >= '0' && c <= '9'; s = 10 * s + (c - '0'), c = getchar());
    return s * w;
}
int T,n,m,p;
int fac[N],inv[N],q[N];
int C(int n,int m){
	if(m>n) return 0;
	return fac[n]*q[m]%p*q[n-m]%p;
}
int Lucas(int n,int m){
	if(!m) return 1;
	return C(n%p,m%p)*Lucas(n/p,m/p)%p;
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	T=read();
	while(T--){
		n=read(),m=read(),p=read();
		fac[0]=1;
		for(int i=1;i<p;i++) fac[i]=fac[i-1]*i%p;
		inv[1]=1;
		for(int i=2;i<p;i++) inv[i]=inv[p%i]*(p-p/i)%p;
		q[0]=1;
		for(int i=1;i<p;i++) q[i]=q[i-1]*inv[i]%p;
		n=n+m;
		printf("%lld\n",Lucas(n,m));
	}
	return 0;
}


拓展

考虑 \(m,n\)\(p\) 进制下的表示,就会发现 \(C^m_n\) 就等于 \(m,n\)\(p\) 进制下每一位对应的组合数的乘积( \(\bmod\)操作就是取最后一位,\(\lfloor \rfloor\)就是左移一位 ) 。

特殊的,当 \(p=2\) 时,组合数有值当且仅当 \(m\)\(n\) 的子集
此时就很高位前缀和 (关于高位前缀和的知识看这)

posted @ 2024-09-02 19:05  Green&White  阅读(12)  评论(0编辑  收藏  举报