卢卡斯定理学习笔记
卢卡斯定理
是一个不用懂证明只记结论的定理
首先我们学卢卡斯定理之前,需要了解的一个东西逆元
逆元博客讲解传送门
在这里,我们需要用到线性求逆元的方法。
我们知道组合数公式Cnm=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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步