秦九韶算法递推公式_算法学习笔记(25): 卢卡斯定理

d289974af0badd680d66eadfaf85dfc0.png

卢卡斯定理是一个与组合数有关的数论定理,在算法竞赛中用于求组合数对某质数的模。在我们谈论卢卡斯定理前,我们先来看看朴素的求组合数的方法有哪些。

如果直接根据定义

直接计算,显然很容易溢出,事实上当m=21时,
就已经大于64位整数可以表示的范围了。当然我们可以边乘边除,但有点麻烦。于是我们有另外一种思路,利用递推式:
(这个递推式可以从
杨辉三角看出)。这种方法相对不容易溢出,时间复杂度为
。其实如果对
精度要求不高的话,最简单快捷的方法是利用 对数。由于 :

所以只需要用

预处理出
的前缀和,即可
求出结果,但可能有浮点误差。

然而,实际上,组合数的增长速度是非常快的,

已经是30位数,
则有89位数,比宇宙中的原子数还多。
(宇宙中的原子数:怎么总是拿我来对比?)所谓递推不容易溢出,那如果结果本身就溢出了,你又怎么办呢?

所幸算法竞赛中的题目常常会要求将结果对某个质数

取模,这样一来,溢出的问题就不用太担心了。我们干脆直接回到最原始的方法:
。只不过,现在我们要把除法变成求逆元,也即:

意义下阶乘和逆元都可以
预处理出来,然后直接
查询即可(实际上不预处理逆元直接
求也绰绰有余)。这基本上是
最常用的求组合数方法。

绕了一圈,怎么还没提到卢卡斯定理呢?嗯……一般来说,这个方法够用了。偏偏,有时候,

可能比
小……

这下麻烦了。如果

小,就不能保证
的逆元存在了(它们可能是
的倍数)。当然还是可以用杨辉三角递推,但
还是太不理想。于是,本文的主角——卢卡斯定理终于要出场了。

卢卡斯定理(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()函数应该都是
的,一共要调用
次,那么复杂度应该是
才对。洛谷上这道模板题的范围才给到
,屈才了。

接下来我们来证明这个式子。如果你对数学推导没有兴趣可以走了(

任意小于
的正整数
,那么 :

由于

是质数,所以
存在模
意义下的逆元,故:

.

显然等号右边是

的倍数,故
.

我们有二项式定理

。由于
除了
的项外模
都为0(上面已证),所以
.

现在我们设

,那么
.

再由二项式定理有

,而同时又有:

.

注意在这个和式中,

恰好能取到
之间的所有整数各一次,所以转而枚举
,得
。结合二项式定理的结果,得
,对比系数,令
,则
,原定理得证。

Pecco:算法学习笔记(目录)​zhuanlan.zhihu.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值