前言
最近遇到一道题,求组合数\(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;
}
就这些了,我们下次再见~