如何快速计算组合数


前言

最近遇到一道题,求组合数\(C(n,m)\mod w\)\(1\leq m\leq n\leq 10^5,1\leq w\leq 10^9\)
这么大的数据,肯定首先想数学方法。

方法

1.瞎搞

第一个:\(C(n,m)=\prod\limits_{i=1}^{m}\frac{n-m+i}{i}\)
那么我们可以把他们边乘边除(这里用到了一个定理:任意\(i\)个连续整数之积一定是\(i\)的倍数),可是因为要除就不能取模,爆了/kk

2.更瞎搞

你说什么?质因数分解?bingo!
我们每次乘和除相当于分解质因数之后对应位置的加减,最后可以得到最终答案是很多个多少的多少次幂相乘,这时就可以快乐的取模了。

代码

首先是输入(那天用的vscode,全是空格):

    long long n,m,x,p,ans=1;
    cin>>n>>m>>p;

然后是质因数分解:

    for(int i=1;i<=m;i++){//利用第一次瞎搞得出的结论
        x=n-m+i;//x是暂存变量
        for(int j=2;j*j<=x;j++) while(x%j==0) x/=j,a[j]++;//分解
        if(x>1) a[x]++;//别忘了这个,一定别忘!
        x=i;//分解第二个,不过这次是要减掉
        for(int j=2;j*j<=x;j++) while(x%j==0) x/=j,a[j]--;//分解过程
        if(x>1) a[x]--;//还是一样的道理
    }

之后就可以乱乘了:

for(int i=2;i<=100000;i++)//枚举每一个数
	if(a[i])//有可以用的
		ans=ans*ksm(i,a[i],p)%p;//建议写一个快速幂,p是题目中的w,注意ans初始值要是1

整体代码:

#include<bits/stdc++.h>
using namespace std;
int a[100005];//存质因数的
long long ksm(long long n,int m,int p){//快速幂
    long long ans=1;
    while(m){
        if(m&1) ans=ans*n%p;
        n=n*n%p;
        m>>=1;
    }
    return ans;
}
int main(){
    long long n,m,x,p,ans=1;//x暂存,ans设成1,nmp对应nmw
    cin>>n>>m>>p;
    for(int i=1;i<=m;i++){//同上
        x=n-m+i;
        for(int j=2;j*j<=x;j++) while(x%j==0) x/=j,a[j]++;
        if(x>1) a[x]++;
        x=i;
        for(int j=2;j*j<=x;j++) while(x%j==0) x/=j,a[j]--;
        if(x>1) a[x]--;
    }
    for(int i=2;i<=100000;i++) if(a[i]) ans=ans*ksm(i,a[i],p)%p;
    cout<<ans<<endl;
    return 0;
}

就这些了,我们下次再见~


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM