http://www.lydsy.com/JudgeOnline/problem.php?id=3656
大意:经过一通推导,问题变成求$$inom N M mod P$$,其中N,M<=1e9, P<=1e5,P可以是合数。
参考这位神犇的博客:http://blog.csdn.net/braketbn/article/details/50752153
作为一个蒟蒻,稍微写一点自己的理解...
如果P是素数,我们可以用lucas定理直接解决,那么P是合数应该怎么办呢?
首先,考虑把P分解质因数,拆成$${p_0} ^ {k_0} {p_1}^{k_1} {p_2} ^ {k_2}...$$的形式,再对每一个p^k计算,最后用CRT(中国剩余定理)合并。
然后就是怎么算组合数对质数的幂取模的问题了。我们设$$f(n)=prod_{i=1}^n i[imod p e0]mod {pk}$$,就是1~n中非p的倍数的积对pk取模,那么只要先把n!,m!,(n-m)!里的p的倍数的因子p全部提出来,然后求出f(n),f(m),f(n-m)的值就行了。
求f(n)可以用递归,把n按照p^k分段,整段的可以一起算,零散的一段直接暴力计算,然后乘上f(n/p),表示所有的p的倍数除以p后f的值。总的时间复杂度$$O(P log N)$$。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define TR(x) printf(#x"=%d
", x)
const int MAXN=100005;
int f[MAXN];
int p[15], k[15], pk[15], s[15], phi[15], np;
int mpow(int a,int b,int m){int r=1;for(;b;b>>=1,a=(ll)a*a%m)if(b&1)r=(ll)r*a%m;return r;}
int calc(int n, int x){
if(!n) return 1;
int q=pk[x], t=n/q, r=1;
if(t){
for(int i=1; i<q; ++i) if(i%p[x]) r=(ll)r*i%q;
r=mpow(r,t,q);
}
for(int i=n%q; i>=1; --i) if(i%p[x]) r=(ll)r*i%q;
return (ll)r*calc(n/p[x], x)%pk[x];
}
int cntp(int n, int p){
int r=0;
while(n) r+=(n/=p);
return r;
}
int main(){
int n, m, P, ans=0;
scanf("%d%d%d", &n, &m, &P);
for(int i=2, tp=P; i<=tp; ++i) if(tp%i==0){
p[np]=i; pk[np]=1;
while(tp%i==0) tp/=i, k[np]++, pk[np]*=i;
phi[np]=pk[np]/i*(i-1); np++;
}
for(int i=0; i<np; ++i){
int t=cntp(n,p[i])-cntp(m,p[i])-cntp(n-m,p[i]);
s[i]=mpow(p[i],t,pk[i]);
s[i]=(ll)s[i]*calc(n,i)%pk[i];
s[i]=(ll)s[i]*mpow(calc(m,i),phi[i]-1,pk[i])%pk[i];
s[i]=(ll)s[i]*mpow(calc(n-m,i),phi[i]-1,pk[i])%pk[i];
}
for(int i=0; i<np; ++i){
ans=(ans+(ll)s[i]*(P/pk[i])%P*mpow(P/pk[i],phi[i]-1,pk[i])%P)%P;
}
printf("%d
", ans);
return 0;
}