Lucas

卢卡斯定理/Lucas 定理:

Lucas 定理用于求解大组合数取模的问题,其中模数必须为素数。正常的组合数运算可以通过递推公式求解,但当问题规模很大,而模数是一个不大的质数的时候,就不能简单地通过递推求解来得到答案,需要用到 Lucas 定理。

对于质数 \(p\) 有:

\[\binom{n}{m}\bmod p=\binom{\lfloor n/p\rfloor}{\lfloor m/p\rfloor}\times \binom{n\bmod p}{m\bmod p} \bmod p \]

对于第二部分可以直接求组合数,对于第一部分可以继续递归 \(Lucas\)
单次时间复杂度 \(O(p\log p)\) ,可以通过预处理达到 \(O(p+\log p)\)

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;

inline ll read(){
	ll s=0,k=1;
	char c=getchar();
	while(c>'9'||c<'0'){
		if(c=='-')k=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		s=(s<<3)+(s<<1)+(c^48);
		c=getchar();
	}
	return s*k;
}

const int N=1e5+10;
ll T,n,m,p,a[N],b[N];

ll ksm(ll a,ll b){
	ll t=1;
	for(;b;b>>=1,a=a*a%p)
		if(b&1) t=t*a;
	return t;
}

ll init(ll n,ll p){
	a[0]=1;
	for(int i=1;i<=n;i++) a[i]=(a[i-1]*i)%p;
	b[n]=ksm(a[n],p-2);
	for(int i=n-1;i>=0;i--){
		b[i]=b[i+1]*(i+1)%p;
	}
}

ll C(ll n,ll m,ll p){
	if(m>n) return 0;
	return a[n]*b[m]%p*b[n-m]%p;
}

ll Lucas(ll n,ll m,ll p){
	if(!m) return 1;
	return (C(n%p,m%p,p)*Lucas(n/p,m/p,p))%p;
}

int main(){
	T=read();
	while(T--){
		n=read();m=read();p=read();
		init(p-1,p);
		printf("%lld\n",Lucas(n+m,m,p)%p);
	}
	return 0;
}
posted @ 2024-07-02 19:28  programmingysx  阅读(4)  评论(0编辑  收藏  举报