组合数取模

来源:互联网 发布:java与xml 第3版 pdf 编辑:程序博客网 时间:2024/06/04 01:33

[组合数取模](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;}
原创粉丝点击