返回顶部

卢卡斯定理

众所周知,lucas定理为:

\(C^m_n\bmod p=C^{m/p}_{n/p}* C^{m\%p}_{n\%p}\bmod p\)(p为质数)

理解一下的话,可以认为是将n,m转为p进制,之后按位取组合数,最后乘起来

就好了。具体实现时只需要写个递归函数。

简单易懂,那本文就到此结束了。

举个栗子

\(C^7_8\bmod 2\)

第一步:

将1和3转化为p(2)进制:\(7=0111, 8=1000\)

第二步:

求出按位的组合数即 \(C^0_1\)\(C^0_1*3\)

最后一步:

算出ans即 \(\prod^p_1\)(刚刚求得组合数)

这样就轻松的算出0了23333


具体实现的可以使用递归函数,还是很好写的。

int lucas(int nn,int mm)
{
	if(!mm) return 1;
	return (lucas(nn/p,mm/p)*C(nn%p,mm%p))%p;
}

注意求得时候需要用一下逆元(不用的童鞋请转乘法逆元


但为什么这定理是对的呢(卢卡斯知道

我们来证明一下:

首先设 \(n=ap+b,m=cp+d ( a=\lfloor n/p\rfloor,c=\lfloor m/p\rfloor\));

然后来看一个二项式在质数模意义下的小性质: \((1+x)^p=1+x^p\); ( 证明很容易,通过费马小定理两边都可以变成1+x)。

然后我们从二项式的角度思考(假定所有操作在模p意义下进行):
\((1+x)^n=(1+x)^{ap}*(1+x)^b=(1+x^p)^a*(1+x)^b=(\sum^a_{i=0}C^i_a*x^{pi})*(\sum^b_{j=0}C^j_b*x^j)\)
(用到了基本的二项式定理)

之后可以很natural的发现\(x^m\)前的系数为\(C^m_n\)

\(C^m_n*x^m=C^c_a*x^{cp}*C^d_b*x^d\)

化简一下:\(C^m_n=C^c_a*C^d_b\)

\(C^m_n=C^{m/p}_{n/p}*C^{m\%p}_{n\%p}\)

证毕


接下来便是代码环节了

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,p;
int jc[210000],ni[210000];
int ksm(int a,int b)
{
	int tmp=1;
	while(b)
	{
		if(b&1) tmp=(tmp*a)%p;
		a=(a*a)%p;
		b>>=1; 
	}
	return tmp;
}
int inv(int x)
{
	return ksm(x,p-2);
}
int C(int a,int b)
{
	if(a<b) return 0;
	return (jc[a]*inv(jc[b])%p*inv(jc[a-b]))%p;
} 
int lucas(int nn,int mm)
{
//	assert(nn!=0);
	if(!mm) return 1;
	return (lucas(nn/p,mm/p)*C(nn%p,mm%p))%p;
}

signed main()
{
	int T;
	cin>>T;
	while(T--)
	{
		scanf("%lld%lld%lld",&n,&m,&p);
		n=n+m;
		jc[0]=1;
		for(int i=1;i<=n;i++) jc[i]=(jc[i-1]*i)%p;
		printf("%lld\n",lucas(n,m));
	}
	
}

完结撒花!!

posted @ 2021-01-15 22:46  djh233  阅读(188)  评论(0)    收藏  举报
Live2D