1. 程式人生 > 實用技巧 >20201116 Day4 盧卡斯定理

20201116 Day4 盧卡斯定理

Lucas 定理內容如下:對於質數 \(p\) ,有

\[\binom{n}{m}\bmod p = \binom{\left\lfloor n/p \right\rfloor}{\left\lfloor m/p\right\rfloor}\cdot\binom{n\bmod p}{m\bmod p}\bmod p \]

觀察上述表示式,可知 \(n\bmod p\)\(m\bmod p\) 一定是小於 \(p\) 的數,可以直接求解, \(\displaystyle\binom{\left\lfloor n/p \right\rfloor}{\left\lfloor m/p\right\rfloor}\)

可以繼續用 Lucas 定理求解。這也就要求 \(p\) 的範圍不能夠太大,一般在 \(10^5\) 左右。邊界條件:當 \(m=0\) 的時候,返回 \(1\)

時間複雜度為 \(O(f(p) + g(n)\log n)\) ,其中 \(f(n)\) 為預處理組合數的複雜度, \(g(n)\) 為單次求組合數的複雜度。

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn=1e5+10;
#define int long long
int n,m,p,T;
int a[maxn];
int pow(int y,int z,int p){
	y%=p;int ans=1;
	for(int i=z;i;i>>=1,y=y*y%p) if(i&1) ans=ans*y%p;
	return ans;	
}
int C(int n,int m){
	if(m>n) return 0;
	return ((a[n]*pow(a[m],p-2,p))%p*pow(a[n-m],p-2,p)%p);	
}
int Lucas(int n,int m){
	if(!m) return 1;
	return C(n%p,m%p)*Lucas(n/p,m/p)%p;	
}
int read(){
	int a=0,op=1;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') op=-1;c=getchar();}
	while(c>='0'&&c<='9') a*=10,a+=c^48,c=getchar();
	return a*op;	
}
#undef int
int main(){
	#define int long long
	T=read();
	while(T--){
		n=read(),m=read(),p=read();
		a[0]=1;
		for(int i=1;i<=p;i++) a[i]=(a[i-1]*i)%p;
		printf("%lld\n",Lucas(m+n,n));
	}
    return 0;
}