求组合数的几种常规方法

在比赛中常用到的几种处理组合数的方法。

1.杨辉三角

利用组合数性质c(n,m)=c(n-1,m)+c(n-1,m-1)与边界条件c(n,0)=c(n,n)=1,在O(n2)的时间复杂度内处理出1-n范围内的所有组合数。

例:给定n,m,k,对于所有的0≤i≤n,0≤j≤min(i,m),求有多少对 (i, j) 满足是c(i,j)是k的倍数。

利用杨辉三角预处理出1-n范围内所有组合数%k的值,再统计出0的个数即可。

void init(){for(int i=0;i<=N;i++) c[i][0]=c[i][i]=1;for(int i=1;i<=N;i++)for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1];
}

适用条件:数据范围较小时,且单次计算常数小(直接从数组读取),在数据范围较小时是最佳选择。

2.线性递推得同一行的所有组合数

考虑c(n,m)= n ! m ! ( n − m ) ! \frac{n!}{m!(n-m)!} m!(n−m)!n!​,c(n,m+1)= n ! ( m + 1 ) ! ( n − m − 1 ) ! \frac{n!}{(m+1)!(n-m-1)!} (m+1)!(n−m−1)!n!​= n − m m + 1 \frac{n-m}{m+1} m+1n−m​ n ! m ! ( n − m ) ! \frac{n!}{m!(n-m)!} m!(n−m)!n!​= n − m m + 1 \frac{n-m}{m+1} m+1n−m​c(n,m),可以得到c(n,m+1)= n − m m + 1 \frac{n-m}{m+1} m+1n−m​c(n,m),可以以c(n,0)=1为基础在O(n)时间内递推出一行内的所有组合数的值。

void init(){c[0]=1;for(int i=1;i<=N;i++) c[i]=c[i-1]*(n-i+1)/i;//c(N,i)
}

适用条件:所需求组合数底数固定。

3.组合数取模(模数比较大且为质数时),预处理阶乘与逆元

若需要计算的组合数非常大,例如c(100000,50000)在%1e9+7下的值,采用o(n2)的杨辉三角递推显然行不通,考虑组合数的定义式 n ! m ! ( n − m ) ! \frac{n!}{m!(n-m)!} m!(n−m)!n!​,可以先O(nlogmod)(mod为膜数)预处理出1-n范围内的所有阶乘与之对应的逆元,处理结束后可以在O(1)的时间内计算出范围内的任意组合数。

ll pow(ll a,ll b){ll sum=1;while(b){if(b&1) sum=sum*a%mod;a=a*a%mod;b>>=1;}return sum;
}void init(){fac[0]=fac[1]=1;for(ll i=2;i<=N;i++){fac[i]=fac[i-1]*i%mod;inv[i]=pow(fac[i],mod-2);}
}ll c(ll n,ll m){return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

适用条件:数据范围较大且模数较大时,但单次计算具有一定常数(还包括取模运算)。

4.组合数取模(模数比较小且为质数时),Lucas定理

若需要计算的环境下膜数非常小,例如求c(n,m)%3,若直接预处理阶乘与逆元,则会发现3!之后处理出来的阶乘数据全部为0,显然得不到结果,而数据范围较大时,采用杨辉三角递推的复杂度也不合适。这时一般采用Lucas定理Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)来计算,其中Lucas(n,m,p)表示c(n,m)%p,时间复杂度为O(plogpn)。

ll pow(ll a,ll b){ll sum=1;while(b){if(b&1) sum=sum*a%mod;a=a*a%mod;b>>=1;}return sum;
}ll c(ll n,ll m){if(n<m) return 0;if(m>m-n) m=n-m;ll a=1,b=1;for(ll i=0;i<m;i++){a*=n-i;a%=mod;b*=i+1;b%=mod;}return a*pow(b,mod-2)%mod;
}ll lucas(ll n,ll m){if(m==0) return 1;return lucas(n/mod,m/mod)*c(n%mod,m%mod)%mod;
}

适用条件:数据范围较大且模数较小时,但单次计算具有一定复杂度。

组合数的几种常规求法相关推荐

  1. Lucas定理与大组合数的取模的求法总结

    Lucas定理与大组合数的取模的求法总结 分类: ACMer 数学 2012-03-11 09:38  1219人阅读  评论(0)  收藏  举报 c 首先给出这个Lucas定理: A.B是非负整数 ...

  2. 光纤收发器的原理及应用_光纤收发器的几种常规应用

    光纤收发器本质上只是完成不同介质间的数据转换,可实现0-100KM内两端计算机或交换机之间的连接,但实际应用中却有着更多扩展,那么,光纤收发器具体有哪些应用呢?接下来我们就跟随飞畅科技的小编一起来详细 ...

  3. 计算机硬盘接口分类,硬盘接口几种常规的类型

    原标题:硬盘接口几种常规的类型 1.并行接口与串行接口. (1)并行接口. 指并行传输的接口,如有0~90个数字,使用10条传输线,每条线只需传输1位数字即可完成. 理论上,并行传输效率高,但由于线路 ...

  4. 总结两种常规地磁匹配算法MAGCOM算法/ICCP算法的特性

    文章目录 前言 一,MAGCOM算法 二,ICCP算法 三,MAGCOM算法与ICCP算法的对比 四,总结 前言 华为高精度定位服务使用GNSS定位,实时动态差分等技术,提供亚米级定位服务,实现了车道 ...

  5. 一种常规的四芯或多芯排线的线序检测电路

    一种常规的四芯或多芯排线的线序检测电路 大家好! 工厂生产或电子产品出厂检测时通常会需要检测一下四芯或多芯电缆两端接线或焊接是否正常,一些人工失误导致多芯线焊接交叉,缺焊的情况需要出厂前筛选出来,这里 ...

  6. html5常见使用的属性,HTML5常见五种常规全局属性

    HTML5中有很多的属性,新增了一个HTML中没有的属性:全局属性. 接下来一起看HTML5常见五种常规全局属性. contentEditable属性 contentEditable是由微软开发.被其 ...

  7. 【算法】计算组合数的四种常用方法

    [算法]计算组合数的四种常用方法 算法一:Cab=Ca−1b−1+Ca−1bC_{a}^{b}=C_{a-1}^{b-1}+C_{a-1}^{b}Cab​=Ca−1b−1​+Ca−1b​ 解析: Ca ...

  8. c语言计算二次函数的原函数公式,二次函数的四种表达式求法推导(20201127010605).docx...

    爱上数学提高素养 爱上数学提高素养 二次函数的四种表达式求法推导 整理于2018418夜 (1 (1)如果二次函数的图像经过已知三点,则设表达式为 三元一次方程组求a.b.c. y .= ax2 bx ...

  9. 总结组合数的几种求法(模板)

    目录 way1.打表C(n,m) way2. 阶乘无模 way3.乘法逆元+快速幂+阶乘 way4.Lucas定理 way1.打表C(n,m) 原理: 杨辉三角 \(\sum_{i=m}^{n}C_{ ...

最新文章

  1. Windows不用虚拟机或双系统,轻松实现linux shell环境:gitforwindows
  2. java里面怎么导入sql文件_java将SQL文件导入到数据库
  3. 兰州办着眼大数据切实提升数据分析管理能力
  4. ios 刷新头像_iOS上传图片到网上,并更新到服务器,常用在设置头像
  5. hive 的分隔符、orderby sort by distribute by的优化
  6. eclipse git插件配置
  7. Unity3D游戏开发初探—1.跨平台的游戏引擎让.NET程序员新生
  8. 深度学习在CTR预估的应用
  9. matlab遗传算法无人机问题,基于改进遗传算法的无人机路径规划
  10. 现代网络管理员必备技能
  11. oracle,EBS的库存数据视图
  12. python3-关于GitHub的最基本操作
  13. 【RSLogix5000】—(1.1)—厂房ControlLogix系统介绍(硬件介绍)——原理
  14. 哪些机器学习模型需要归一化
  15. UINO优锘:EMV,让IT告警实现至繁归于至简
  16. 一衣带水 守望相助:中日夫妻七七再行慈善之旅
  17. 爱上文案——如何写出有销售力的广告文案
  18. python:网络数据收集
  19. ant design vue table分页
  20. 有些事情,现在不想就晚了

热门文章

  1. 深入浅出搞懂网络的五层协议
  2. Maven之(四)Maven命令
  3. linux系统自动清理日志实现脚本
  4. ubuntu下IPv6查询相关命令
  5. IP ID idle 扫描
  6. 维度建模的基本概念及过程
  7. 分布式事务--TX-LCN(介绍)
  8. 怎么实时查看mysql当前连接数呢
  9. 忘了Linux服务器密码怎么办
  10. Gitlab的branch与Tag的使用