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;
}