卢卡斯定理学习笔记

本文深入解析了卢卡斯定理及其在组合数求解中的应用,通过分解大问题为小问题的乘积,利用逆元概念简化复杂计算。提供了详细的算法实现代码,包括线性求逆元和卢卡斯定理的应用。

卢卡斯定理

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

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

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

我们知道组合数公式\[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;
}

转载于:https://www.cnblogs.com/ifmyt/p/9776655.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值