求组合数的几种方法
引入
在做题时,经常会遇到需要计算从 个物品中选择 个的方案数的情况。
我们就需要用到计算组合数的公式:。这篇文章里为了方便,用 代替 。
方法
1.乘法逆元
最简单的方法莫过于用组合数公式直接求。
但在 就已经爆 long long,所以通常是会有模数的。
如果有了模数,那么普通的除法肯定是会出大问题,就又要引入逆元。
如果一个线性同余方程 ,则 称为 的逆元,记作 。
定义 ( 为模数),。那么我们就可以把原始公式转换成 。
在这里,求乘法逆元可以用线性递推法 。
const int mod = ...;
int inv[],fact[],infact[];
void init(){
fact[1]=infact[1]=inv[1]=1;
for(int i=2;i<=...;i++){
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*inv[i]%mod;
}
}
int C(int n,int m){
return (ll)fact[n]*infact[n-m]%mod*infact[m]%mod;
}
顺带一提
那么既然 不爆 long long,在 的情况下你也可以写暴力。
2.预处理
在 和 都不是很大的情况下,如 ,可以直接杨辉三角预处理
const int mod = ...;
int C[][];
void init(){
C[0][0]=1;
for(int i=1;i<=...;i++)
for(int j=0;j<=i;j++)
if(j)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
else C[i][j]=1;
}
int C(int n,int m){
return C[n][m];
}
3.Lucas定理
一般用于 , 很大, 比较小的情况。
Lucas定理的证明和公式均在这篇博客中有,可自行跳转阅读,此处不再赘述。
具体求组合数的方法可以根据 的范围自行选择,我这里选择了乘法逆元法。
const int mod = ...;
int inv[],fact[],infact[];
void init(){
fact[1]=infact[1]=inv[1]=1;
for(int i=2;i<=...;i++){
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*inv[i]%mod;
}
}
int C(int n,int m){
return (ll)fact[n]*infact[n-m]%mod*infact[m]%mod;
}
int Lucas(int n,int m){
if(n<mod)return C(n,m);
return C(n%mod,m%mod) * Lucas(n/mod,m/mod) %mod;
}
本文作者:ASnown
本文链接:https://www.cnblogs.com/As-Snow/p/17223075.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步