卢卡斯定理学习笔记

卢卡斯定理

是一个不用懂证明只记结论的定理

首先我们学卢卡斯定理之前,需要了解的一个东西逆元

逆元博客讲解传送门
在这里,我们需要用到线性求逆元的方法。

我们知道组合数公式\[C^n_m=\frac{n!}{m!(n-m)!}\]
卢卡斯定理的公式是
\[ C^m_n=\prod^k_{i=0}C^{m_i}_{n_i}\pmod p \\ n=n_{k}p^{k}+n_{k-1}p^{k-1}+\dots +n_{1}p+n_0 \\ m=m_{k}p^{k}+m_{k-1}p^{k-1}+\dots +n_{1}p+n_0 \\ \]

这样将组合数的求解分解为小问题的乘积,下面考虑计算C(ni, mi) %p.

已知\(C(n, m)\ mod\ p = \frac{n!}{m!(n - m)!}\ mod\ p\)。当我们要求(a/b)mod p的值,且b很大,无法直接求得a/b的值时,我们可以转而使用乘法逆元k,将a乘上k再模p,即(a*k) mod p。 其结果与(a/b) mod p等价。

于是,简单的代码就这样出现了

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cstdio>
#define LL long long
using namespace std;
int k,n,m,p;
LL inv[100005],fac[100005];
LL lucas(int x,int y) {
    if(x<y) return 0;
    else if(x<p) return fac[x]*inv[y]*inv[x-y]%p;
    else return lucas(x/p,y/p)*lucas(x%p,y%p)%p;
}
void get() {
    inv[1]=1;
    fac[1]=1;
    fac[0]=1;
    inv[0]=1;
    for(int i=1; i<=n+m; i++)fac[i]=fac[i-1]*i%p;
    for(int i=2; i<=n+m; i++)inv[i]=(p-p/i)%p*inv[p%i]%p;
    for(int i=2; i<=n+m; i++)inv[i]=inv[i-1]*inv[i]%p;
}
int main() {
    scanf("%d%d%d",&n,&m,&p);
    get();
    printf("%d\n",lucas(n,m)%p);
        return 0;
}

猜你喜欢

转载自www.cnblogs.com/ifmyt/p/9776655.html