卢卡斯(Lucas)定理

蓝咒 提交于 2020-01-26 19:16:50

问题

洛谷 题目地址

给你正整数 \(n,m,p\),其中 \(p\) 是质数。求 \(\dbinom{n}{m} \% p\)\(\dbinom{n}{m}\) 是组合数,表示 \(n\) 选出 \(m\))。

Lucas定理结论

\(p\) 是质数,则对于任意整数 \(1 \le m \le n\),有:

\[\dbinom{n}{m} \equiv \dbinom{n\%p}{m\%p} * \dbinom{n/p}{m/p} \pmod{p}\]

另一种形式:

\(n=\sum_{i=0}^k n_i*p^i,m=\sum_{i=0}^k m_i*p^i\)(相当于把 \(n,m\) 写成 \(p\) 进制数),那么有:

\[\dbinom{n}{m} \equiv \prod_{i=0}^k \dbinom{n_i}{m_i} \pmod{p}\]

很显然两种形式意义相同,我们直接证明后面这个式子。

证明

前置知识:多项式同余

前置知识:二项式定理

推导:

\[\because (1+x)^p = \sum_{i=0}^p \dbinom{p}{i} 1^{p-i}*x^i = \sum_{i=0}^p \dbinom{p}{i} x^i\]

\[\therefore (1+x)^p \equiv \sum_{i=0}^p \dbinom{p}{i} x^i \pmod{p}\]

把首项尾项都拎出来,因为中间的项都至少含有一个因子 \(p\),所以在 \(\bmod p\) 意义下为零,因此有:

\[(1+x)^p \equiv 1+x^p \pmod{p}\]

\[\because (1+x)^n = \prod_{i=1}^k (1+x)^{n_i*p^i}\]

利用上面这个结论:

\[\therefore (1+x)^n \equiv \prod_{i=1}^k (1+x^{p^i})^{n_i} \pmod{p}\]

我们又知道 \(\dbinom{n}{m}\) 表示 \((1+x)^n\) 的展开式中 \(x^m\) 的系数。

\(\dbinom{n_i}{m_i}\) 表示 \((1+x^{p^i})^{n_i}\) 的展开式中 \(x^{m_i*p^i}\)

\(\dbinom{n_0}{m_0} x^{m_0*p^0},...,\dbinom{n_k}{m_k} x^{m_k*p^k}\) 相乘,可以得到 \(\dbinom{n_0}{m_0}*...*\dbinom{n_k}{m_k} x^m = (\prod_{i=0}^k \dbinom{n_i}{m_i}) x^m\)

回到前面这个同余式,根据多项式同余定理,两个式子同余,那么就有两个多项式的 \(x^m\) 这项的系数同余,于是就有:

\[\dbinom{n}{m} \equiv \prod_{i=0}^k \dbinom{n_i}{m_i} \pmod{p}\]

证毕。

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
    int x=0,f=1; char ch=getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 1e5+7;
int n,m,p;
int fac[N];
int Pow(int x,int y) {
    int res = 1, base = x;
    while(y) {
        if(y&1) res = res*base%p; base = base*base%p; y >>= 1;
    }
    return res;
}
int C(int a,int b) {
    if(a < b) return 0;
    return fac[a] * Pow(fac[b],p-2) % p * Pow(fac[a-b],p-2) % p;
}
int Lucas(int a,int b) {    // (a,b)
    if(!b) return 1;
    return C(a%p,b%p) * Lucas(a/p,b/p) % p;
}
void work() {
    n = read(), m = read(), p = read();
    fac[0] = 1;
    for(int i=1;i<=p;++i) fac[i] = fac[i-1] * i % p;
    printf("%lld\n",Lucas(n+m,m));
}
signed main()
{
    int T = read();
    while(T--) work();
    return 0;
}

感谢

感谢 NCC-79601 【学习笔记】卢卡斯定理Combatting 卢卡斯定理(十分钟带你看懂) 帮助我学会了卢卡斯定理。

感谢您的阅读,您的点赞是对我最大的支持!

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!