[组合数取模](com.cpp/in/out 1s 128M)
题目描述
给出N,M,P,求C(N,M) Mod P
1<=M<=N<=10^6,1<=P<=10^5,P可能为合数
输入格式:一行给出N,M,P
输出格式:一行,输出最终结果
样例输入
5 2 3
样例输出
分析:此题m与n很大,如果求暴力分分钟wa,P可能为合数,用欧拉定理求逆元似乎是可行的,未写代码证明。但是我们可以通过唯一分解定理将n!/(m!*(n-m)!)其分解成一个个质因数的指数次方,然后用这些质因子的指数次方MOD P。这个指数这样求得:
设N!里含有的质因子ai,它在N!里有x个质因子ai;
M!里含有的质因子ai,它在M!里有y个质因子ai;
(N-M)!里含有的质因子ai,它在(N-M)!里有z个质因子ai;
那么就可以由 ai ^ (x-(y+z)) % P;
将所有的这样的质因子都一一的枚举出来,如上面的方法计算其指数,逐个MOD P,就可以比较好的计算出这道题。
具体程序代码为:
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
int n,m,P;
int p[1000006],tot;
bool isPrime[1000006];
void aishi() //埃式筛质数
{
memset(isPrime,true,sizeof(isPrime));
int t = sqrt(n);
for(int i= 2; i<= t; i++){
if(isPrime[i]){
for(int j = i*i; j<= n; j+=i){
isPrime[j] = false;
}
}
}
for(int i = 2; i<= n; i++){
if(isPrime[i]) p[++tot] = i;
}
}
LL quickMod(LL a,LL b){ //快速幂
LL ans = 1;
a = a % P;
while(b){
if(b%2 == 1) ans = ans * a % P;
b = b/2;
a = a * a % P;
}
return ans;
}
int num(int x,int k) //计算1~x之间的数中约数k的个数
{
int ans = 0;
while(x){
ans += (x/k);
x = x/k;
}
return ans;
}
LL solve(){
LL ans = 1;
for(int i = 1;i<= tot&& p[i] <= n; i++)
{
int x = num(n,p[i]);
int y = num(m,p[i]);
int z = num(n-m,p[i]);
ans = (ans * quickMod(p[i],x-(y+z))) % P;
}
return ans;
}
int main(){
cin >> n >> m >> P;
aishi();
LL ans = solve();
cout << ans << endl;
return 0;
}