1. 程式人生 > 其它 >【luogu P3807】【模板】盧卡斯定理/Lucas 定理(含 Lucas 定理證明)

【luogu P3807】【模板】盧卡斯定理/Lucas 定理(含 Lucas 定理證明)

求 C(n,n+m)%p 的值。 p 保證是質數。

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

題目連結:luogu P3807

題目大意

求 C(n,n+m)%p 的值。
p 保證是質數。

思路

Lucas 定理內容

對於非負整數 \(n\),\(m\),質數 \(p\),有:
\(C_m^n\equiv \prod\limits_{i=0}^kC_{m_i}^{n^i}(\bmod\ p)\)
其中 \(m=m_kp^k+...+m_1p+m_0\)\(n=n_kp^k+...+n_1p+n_0\)。(其實就是 \(n,m\)\(p\) 進位制展開)

那我們一般做題用的是遞推式,也就是 \(C_m^n\equiv C_{\left\lfloor m/p\right\rfloor}^{\left\lfloor n/p\right\rfloor}C_{m\bmod p}^{n\bmod p}(\bmod\ p)\)


(當 \(n>m\) 時,我們規定 \(C_{m}^n=0\)

啥時候會用

我們有時候要算組合數,可能 \(C_{m}^n\)\(n,m\) 很大,這時候一般做題就會讓他取模一個數 \(\bmod \ p\)

那如果 \(p>m\),我們可以愉快的用這個式子求:\(C_{m}^n=\dfrac{m!}{n!(m-n)!}\)
算出 \(n!\)\((m-n)!\) 的逆元,就可以搞。

可當 \(m\geqslant p\) 的時候,分母的乘法逆元可能不存在。(因為 \(x\)\(p\) 的倍數的話 \(x\) 就沒有模 \(p\) 的逆元)

那這個時候我們就可以用 Lucas 定理把這個組合數拆成幾個 \(m<p\)

的,就可以搞了。

證明

證明 Lucas 定理之前,我們先證明兩個式子。


式一:

\(C_p^i\equiv \frac{p}{i}C_{p-1}^{i-1}\equiv0(\bmod\ p),(1\leqslant i<p)\)
證明:
\(C_p^i=\dfrac{p!}{i!(p-i)!}=\dfrac{p}{i}\dfrac{(p-1)!}{(i-1)!(p-1-(i-1))!}=\frac{p}{i}C_{p-1}^{i-1}\)
由於 \(1\leqslant i<p\),故 \(i\) 會有 \(p\) 的逆元 \(inv_i\)
\(\frac{p}{i}C_{p-1}^{i-1}=p\times inv_i\times C_{p-1}^{i-1}\)


那這個地方都是 \(p\) 的倍數,那它被 \(p\) 取模一定是 \(0\),故得證。


式二:

根據二項式定理:
\((1+n)^p\equiv C_p^0+C_p^1x+...+C_p^{p-1}x^{p-1}+C_p^px^p(\bmod\ p)\)
再根據式一 \(C_p^i\equiv0(\bmod\ p),(1\leqslant i<p)\),可以得到
\((1+n)^p\equiv C_p^0+C_p^px^p\equiv 1+x^p(\bmod\ p)\)


接著我們開始證明,先設 \(\left\lfloor m/p\right\rfloor=q_m,\left\lfloor n/p\right\rfloor=q_n,m\bmod p=r_m,n\bmod p=r_n\)
那有 \(m=q_mp+r_m,n=q_np+r_n\)

接著我們繼續用二項式定理:
\((1+x)^m=\sum\limits_{i=1}^{m}C_{m}^ix^i\)
然後我們把左邊給化簡:
\(\begin{aligned}(1+x)^m & =(1+x)^{q_mp+r_m} \\ & =(1+x)^{q_mp}\cdot(1+x)^{r_m} \\& =[(1+x)^p]^{q_m}\cdot(1+x)^{r_m}\\&\equiv(1+x^p)^{q_m}\cdot(1+x)^{r_m}[式二]\\&\equiv\sum\limits_{i=1}^{q_m}C_{q_m}^ix^{ip}\sum\limits_{i=1}^{r_m}C_{r_m}^{i}x^i(\bmod\ p)\end{aligned}\)

那就有:
\(\sum\limits_{i=1}^{m}C_{m}^ix^i\equiv\sum\limits_{i=1}^{q_m}C_{q_m}^ix^{ip}\sum\limits_{i=1}^{r_m}C_{r_m}^{i}x^i(\bmod\ p)\)

那對於任意一個數 \(z\),必然會有一組 \(i,j\) 滿足 \(x^z=x^{pi}x^j\)
不難看出這其實就是滿足 \(z=pi+j\),所以當且僅當 \(i=\left\lfloor \dfrac{z}{p}\right\rfloor,j=z\bmod p\)
那也就是說左邊的 \(i\) 取任意一個,右邊都有一個新的跟它對於恆等。
左邊 \(i=x\),右邊的就分別是 \(i=\left\lfloor \dfrac{x}{p}\right\rfloor,i=x\bmod p\)

那當 \(i=x\),就有:
\(\begin{aligned}C_m^nx^n&=C_{q_m}^{q_n}x^{q_np}C_{r_m}^{r_n}x^{r_n}\\C_m^nx^n&=C_{q_m}^{q_n}C_{r_m}^{r_n}x^{q_np+r_n}\\C_m^nx^n&=C_{q_m}^{q_n}C_{r_m}^{r_n}x^n\end{aligned}\)
兩邊同乘 \(inv(x^n)\),就有了 \(C_{m}^n=C_{q_m}^{q_n}C_{r_m}^{r_n}\)

得證。

本題

其實逆元的話直接要用的話直接 \(x^{p-2}\bmod p\) 更好,不用像我這樣線性求出每個。

程式碼

#include<cstdio>
#define ll long long

using namespace std;

ll T, n, m, p;
ll jc[100001], inv[100001];

ll ksm(ll x, ll y) {
	ll re = 1;
	while (y) {
		if (y & 1) re = (re * x) % p;
		x = (x * x) % p;
		y >>= 1;
	}
	return re;
}

ll C(ll x, ll y) {//暴力算組合數
	if (x > y) return 0;
	return ((jc[y] * inv[x]) % p * inv[y - x]) % p;
}

ll work(ll n, ll m) {//Lucas 定理
	if (!n) return 1;
	return (work(n / p, m / p) * C(n % p, m % p)) % p;
}

int main() {
	scanf("%lld", &T);
	while (T--) {
		scanf("%lld %lld %lld", &n, &m, &p);
		jc[0] = 1;
		for (ll i = 1; i <= p; i++)//預處理階乘與其逆元
			jc[i] = (jc[i - 1] * i) % p;
		inv[p - 1] = ksm(jc[p - 1], p - 2);
		for (ll i = p - 2; i >= 0; i--)
			inv[i] = (inv[i + 1] * (i + 1)) % p;
		
		printf("%lld\n", work(n, n + m));
	}
	
	return 0;
}