前言
最近遇到一道題,求組合數\(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;
}
就這些了,我們下次再見~