【算法学习笔记】组合数与 Lucas 定理
卢卡斯定理是一个与组合数有关的数论定理,在算法竞赛中用于求组合数对某质数的模。
第一部分是博主的个人理解,第二部分为 Pecco 学长的介绍
一篇很好的 卢卡斯定理 博文
第一部分
一般情况下,我们计算大组合数取模问题是用递推公式进行计算的:
其中p相对较小的素数。但是当n和m过大时,计算的耗费就急剧增加
其中:
证明方法也很简单,主要用到如下等式:
应用这个公式,可以的到如下递归式
这里的
如果上面的解释没有理解的话请往下看一下 Pecco 学长的介绍
第二部分
开篇说的就很清楚了,
卢卡斯定理是一个与组合数有关的数论定理,在算法竞赛中用于求组合数对某质数的模。
在我们谈论卢卡斯定理前,我们先来看看朴素的求组合数的方法有哪些。
如果直接根据定义 m=21
时,
所以只需要用
然而,实际上,组合数的增长速度是非常快的,
所幸算法竞赛中的题目常常会要求将结果对某个质数
模
绕了一圈,怎么还没提到卢卡斯定理呢?嗯……一般来说,这个方法够用了。偏偏,有时候,
这下麻烦了。如果
卢卡斯定理(Lucas's theorem):
对于非负整数
和质数 , 其中
、 是 和 的 进制展开
但其实,我们一般使用的是这个可以与之互推的式子:
当
就像辗转相除法那样,可以利用这个式子递归求解,递归出口是
// 需要先预处理出fact[],即阶乘
inline ll C(ll m, ll n, ll p) {
return m < n ? 0 : fact[m] * inv(fact[n], p) % p * inv(fact[m - n], p) % p;
}
inline ll lucas(ll m, ll n, ll p) {
return n == 0 ? 1 % p : lucas(m / p, n / p, p) * C(m % p, n % p, p) % p;
}
网上说卢卡斯定理的复杂度是 C()
函数应该都是 的
接下来我们来证明这个式子。如果你对数学推导没有兴趣可以走了(雾
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)
2020-04-14 最小生成树算法【图解】:一文带你理解什么是Prim算法和Kruskal算法