【学习笔记】卢卡斯定理

NCC79601

2019-04-02 13:55:51

Personal

卢卡斯定理是用于求$C_m^n\mod p$的一种算法。 ### 定理 $$C_m^n\equiv C_{m\mod p}^{n\mod p}\times C_{m/p}^{n/p}(\mod p)$$ ### 推导 首先,对于$C_m^n$有: $$C_m^n=\frac{m!}{n!(m-n)!}=\frac{m}{n}\cdot\frac{(m-1)!}{(n-1)!(m-n)!}=\frac mn\cdot C_{m-1}^{n-1}$$ 由二项式定理: $$(a+b)^n=\sum_{i=0}^nC_n^ia^{n-i}b^i$$ 因此可知: $$(1+x)^p\equiv \sum_{i=0}^p C_p^ix^i\equiv C_p^01^px^0+C_p^p1^0x^p\equiv 1+x^p(\mod p)$$ 取首尾两式整理得: $$(1+x)^p\equiv 1+x^p(\mod p)$$ 由这个性质,设$m=k_1p+r_1,n=k_2p+r_2$,有: $$C_m^n\equiv C_{k_1p}^{k_2p}\cdot C_{r_1}^{r_2}(\mod p)$$ 所以从一个二项式开始展开: $$(1+x)^m=(1+x)^{k_1p+r_1}=(1+x)^{k_1p}\cdot(1+x)^{r_1}$$ $$\because (1+x)^{k_1p}\equiv((1+x)^p)^{k_1}\equiv(1+x^p)^{k_1}(\mod p)$$ $$\therefore (1+x)^n\equiv (1+x^p)^{k_1}\cdot(1+x)^{r_1}(\mod p)$$ 找到$x^n$项: $$\because C_m^nx^n\equiv (C_{k_1}^{k_2}\cdot x^{k_2p})( C_{r_1}^{r_2}\cdot x^{r_2})\equiv C_{k_1}^{k_2}\cdot C_{r_1}^{r_2}x^n (\mod p)$$ $$\therefore \text{约掉}x^n\text{可得:}C_m^n\equiv C_{k_1}^{k_2}\cdot C_{r_1}^{r_2}$$ ### 应用 因为卢卡斯定理可以把一个巨大的组合数给拆掉,所以利用这个性质就能够求出$C_m^n \mod p$,也就是说: $$C_m^n\equiv C_{m_0}^{n_0}\cdot C_{m_1p}^{n_1p}\cdot C_{m_2p^2}^{n_2p^2}\cdots (\mod p)$$ $$C_m^n\equiv \prod_{i=0}C_{m_ip^i}^{n_ip_i}(\mod p)$$ 可联想到**快速幂**,先把$m$和$n$拆成$p$进制数,然后直接暴力计算就可以了。 ## 模板 [P3807](https://www.luogu.org/problemnew/show/P3807) **注意:** 题目要求的是$C_{n+m}^m\mod p$,所以输入的时候稍微不太一样。 可以留意如何将一个数拆成$p$进制数,操作较为高级。 --- 代码 ```cpp #include<bits/stdc++.h> using namespace std; const int MAXN = 100010; long long a[MAXN]; int p; inline long long Pow(long long base, int k) { long long res = 1; base %= p; for(register int i = k; i; i >>= 1) { if(i & 1) res = res * base % p; base = base * base % p; } return res % p; } inline long long C(long long m, long long n) { if(m < n) return 0; if(m == n || n == 0) return 1; if(n == 1) return m; return a[m] * Pow(a[n], p-2) % p * Pow(a[m-n], p-2) % p; } inline long long Lucas(long long m, long long n) { if(!n) return 1; return C(m % p, n % p) * Lucas(m/p, n/p) % p; } int main() { int t; scanf("%d", &t); while(t--) { long long m, n; scanf("%lli%lli%lli", &n, &m, &p); a[0] = 1; for(register int i = 1; i <= p; i++) a[i] = (a[i-1] * i) % p; printf("%lli\n", Lucas(n + m, m)); } return 0; } ```