如何快速計算組合數


前言

最近遇到一道題,求組合數\(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