二项式定理
若
n 是正整数,则有:
(x+y)n=i=0∑nCnixiyn−i。
用数学归纳法证明:
假设
n=k 时原式成立,则
n=k+1 时,有:
(x+y)k+1=(x+y)(x+y)k=(x+y)i=0∑kCkixiyk−i=(x+y)(Ck0yk+Ck1xyk−1+⋯+Ckk−1xk−1y+Ckkxk)=xk+1+yk+1+(Ck0+Ck1)xyk+(Ck1+Ck2)x2yk−1+⋯+(Ckk−1+Ckk)xky=xk+1+yk+1+i=1∑k(Cki−1+Cki)xiyk−i+1
因为
Cki−1+Cki=(i−1)!(k−i+1)!k!+i!(k−i)!k!=i!(i−1)!(k−i)!(k−i+1)!k!i!(k−i)!+k!(i−1)!(k−i+1)!=i!(k−i+1)!(k+1)!=Ck+1i
所以
(x+y)k+1=xk+1+yk+1+i=1∑kCk+1ixiyk−i+1=i=0∑k+1Ck+1ixiyk−i+1
因此
n=k+1 时原式也成立,得证。
一个引理
若
p 是素数,则有:
(x+y)p≡xp+yp(modp)。
证明:由二项式定理可知:
(x+y)p=i=0∑pCpixiyp−i。
其中当
1≤i<p 时:
Cpi=i!(p−i)!p!=i×(i−1)!(p−i)!p×(p−1)!=ipCp−1i−1
所以
Cpi≡ipCp−1i−1≡ipCp−1i−1×i×inv(i)≡p×inv(i)Cp−1i−1≡0(modp)
其中
inv(i) 表示
i 在模
p 意义下的乘法逆元。因为
p 是素数且
i<p,故
inv(i) 一定存在。
原式在同余式中只剩下
i=0,p 两项,得证。
Lucas 定理
若
p 是素数,则有:
Cnm≡C⌊pn⌋⌊pm⌋×Cnmodpmmodp(modp)。
证明:
根据带余除法,令
n=ap+b,m=cp+d,其中
0≤b,d<p。
根据二项式定理得到式子一:
(1+x)n≡i=0∑nCnixi(modp)
根据二项式定理和引理得到式子二:
(1+x)n≡(1+x)ap+b≡((1+x)p)a×(1+x)b≡(1+xp)a×(1+x)b≡(i=0∑aCaixip)(j=0∑bCbjxj)(modp)
由式子一可知
xm 的系数为
Cnm。
由式子二可知
xm=xcp+d 的系数为
CacCbd。
因为
⌊pn⌋=a,⌊pm⌋=c,nmodp=b,mmodp=d。
所以
Cnm≡C⌊pn⌋⌊pm⌋×Cnmodpmmodp(modp),证毕。
我们发现在求
Cnmmodp 时不太好求,因为是两个很大的数相除,得转换成乘的形式。
因为
p 是素数,根据费马小定理:
m!p−1≡1(modp),(n−m)!p−1(modp)。
那么有:
Cnm=m!(n−m)!n!≡n!×m!p−2×(n−m)!p−2(modp)
但是这样求是有一点问题的,因为
m! 或者
(n−m)! 可能是
p 的倍数,就不能用费马小定理了。
其实具体实现是可以避免的,因为在求
Cnmodpmmodp(modp) 时费马小定理是肯定成立的,可以直接求。
C⌊pn⌋⌊pm⌋(modp) 虽然没法直接求,但是可以利用 Lucas 定理进一步拆开,直到都能够拆成可求解的形式即可。
Code
CPP#include <stdio.h>
#define int long long
int f[100005];
void init(int a,int p){
f[0]=f[1]=1;
for(int i=2;i<=a;++i)
f[i]=(f[i-1]*i)%p;
}
int pow(int a,int b,int p){
a%=p;
int res=1;
while(b>0){
if(b&1ll) res=(res*a)%p;
a=(a*a)%p;
b>>=1ll;
}
return res;
}
int C(int m,int n,int p){
if(m>n) return 0;
return (f[n]*pow(f[m],p-2,p)%p *pow(f[n-m],p-2,p))%p;
}
int Lucas(int m,int n,int p){
if(!m) return 1;
return C(m%p,n%p,p)*Lucas(m/p,n/p,p)%p;
}
signed main(){
int T; scanf("%lld",&T);
while(T--){
int n,m,p; scanf("%lld%lld%lld",&n,&m,&p);
init(n+m,p);
printf("%lld\n",Lucas(n,n+m,p));
}
}