如何快速计算组合数

前言

最近遇到一道题,求组合数\(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;
}

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

上一篇:洛谷P1154 奶牛分厩(数论)


下一篇:2021牛客OI赛前集训营-方格计数【计数,dp】