卢卡斯定理学习笔记

卢卡斯定理

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

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

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

我们知道组合数公式$$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;
}
posted @ 2018-10-12 10:24  _Lancy  阅读(497)  评论(0编辑  收藏  举报