组合数取模

[组合数取模](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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值