当前位置: 首页 > news >正文

扩展 Lucas 定理学习笔记 详解,一文带你彻底看懂扩展 Lucas 定理。

背景

考虑这样一个问题:

给定 \(n, r, p\) ,求

\[\binom{n}{r} \bmod m \]

不保证 \(m\) 是质数。\(1\le r\le n\le 10^{18}\)

显然此时普通 Lucas 定理就无能为力了。这时需要用到扩展 Lucas 定理(exLucas)。

前置知识

你需要知道:

  • 扩展欧几里得算法(exgcd);

  • 中国剩余定理(CRT);

  • Legendre 公式;

  • 关于“阶乘余数”的递归公式。

约定记号

  • \(v_p(n!)\)\(n!\) 中质因子 \(p\) 的次数;

  • \((n!)_p\)\(n!\) 去掉所有的 \(p\) 因子。

算法流程

  1. 我们将模数 \(m\) 分解质因数:\(m=p_1^{a_1}p_2^{a_2}\cdots p_{n}^{a_n}\)

  2. 我们构造方程组

\[\begin{cases} x \equiv r_1 \pmod{p_1^{a_1}} \\ x \equiv r_2 \pmod{p_2^{a_2}} \\ \cdots \cdots\\ x \equiv r_n \pmod{p_n^{a_n}} \end{cases} \]

  1. 注意到这些方程里的模数两两互质。我们使用 CRT 合并求解 \(x\) ,即为我们想要的答案 \(\binom{n}{r}\bmod m\)

接下来的问题就完全聚焦到了 \(r_i\) 的构造上来。我们考虑如下流程:

  1. 明确我们要构造 \(r_i\equiv \binom{n}{r}\pmod{p_i^{a_i}}\)

  2. 转化问题。我们需要求解 \(\binom{n}{r}\bmod p^a\) ,其中 \(p\) 是质数。即我们要求解如下式子:

\[\frac{n!}{n!(n-r)!}\bmod p^a \]

  1. 注意到分母 \(r!(n-r)!\)\(p^a\) 不一定互质。那么就意味着我们没有办法求分母的逆元,进一步我们无法求解这个分式。考虑我们该如何改写它。

  2. 我们目标是使分母和 \(p^a\) 互质。为此我们将式子改写为:

\[p^v\frac{(n!)_p}{(r!)_p((n-r)!)_p} \bmod p^a \]

​ 其中 \(v=v_p(n!)-v_p(r!)-v_p((n-r)!)\) 。注意到若 \(v\ge a\) ,则结果为 \(0\)

  1. 计算 \(v_p(n!)\) 。这可以利用 Legendre 公式求出:

\[v_p(n!) = \sum_{i=1}^{\infty}\left\lfloor \frac{n}{p^i} \right\rfloor \]

  1. 计算 \((n!)_p\) 。利用前置知识中提到的递归公式:

\[(n!)_p \equiv (\pm1)^{\lfloor n/p^a\rfloor}(\lfloor n/p \rfloor!)_p \left(\prod_{1\le j\le(n\bmod p^a)且j\nmid p}j\right) \pmod{p^a} \]

​ 一些注意事项:

​ i. \((\lfloor n/p\rfloor!)_p\) 可以利用循环来展开递归,加快速度;

​ ii. \(\prod j\) 项,等价于在 \((n\bmod p^a)!\) 中去掉 \(p\) 因子,故这里可以直接预打表,用 f[i] 来表示 \(i!\) 中去掉 \(p\) 因子 的值。这个表只用打到 \(i\le p^a\) 即可。

​ iii. 式子中 \(\pm 1\) 的取值:当且仅当 \(p=2\)\(a\ge 3\) 时,\(\pm1\) 取正;否则取负。

于是我们完成了一个 \(r_i\) 的构造。按此流程,我们对每一个 \(p_i^{a_i}\) 构造一个 \(r_i\) ,就完成了方程组的构造。

复杂度分析

  1. 我们必须分解 \(m\) 。用试除法分解质因数的复杂度是 \(\mathcal O(\sqrt{m})\)
  2. 我们必须处理每个质因子。其中 Legendre 公式和 递归公式 的迭代次数都是 \(\mathcal O(\log_pn)\)

综上,这个算法的复杂度为 \(\mathcal O(\sqrt{m}+\sum\log_{p_i}n)\)

模板代码

这就是对上文思路的完全再现。如果还有不理解的过程,请参见下面的代码。

#include<iostream>
#include<vector>
#define fastio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int long long
#define MIKU 0
using namespace std;int n, m, p;  //求解 C(n,m) mod p。//扩展欧几里得求逆元。
int exgcd(int a, int b, int &x, int &y) {if(b == 0) {x = 1; y = 0; return a;}int d = exgcd(b, a%b, y, x);y -= a / b * x;return d;
}int inv(int a, int m) {int x, y;exgcd(a, m, x, y);return (x % m + m) % m;
}//对每个质因数构造 r_i
struct BPP {int p, a, pa;  //p:模数  a:次数  pa:p^a。vector<int> f;  //预处理 f[i] ,表示 i! 去掉 p 因子。//构造函数,计算出 pa 和 f。BPP(int prime, int power) : p(prime), a(power) {pa = 1;for(int i=1; i<=a; i++) pa *= p;f.resize(pa); f[0] = 1;for(int i=1; i<pa; i++) f[i] = (i % p) ? f[i-1] * i % pa : f[i-1];}//利用 Legendre 公式求 vp(n!)int vp(int n, int cnt = 0) {for(; n; n/=p) cnt += n / p;return cnt;}//利用递归公式求 (n!)_p 。代码中用循环代替递归。int fac(int n, int res = 1) {bool neg = (p != 2 || a < 3);for(; n>1; n/=p) {if((n / pa) & neg) res = pa - res;  //这一步完成符号修正,即公式中第一个因子。res = res * f[n%pa] % pa;}return res;}//利用 vp(n!) 和 (n!)_p 算 C(n,r) mod p^a。 int C(int n, int r) {int v = vp(n) - vp(r) - vp(n-r);if(v >= a) return 0;int res = fac(n) * inv(fac(r) * fac(n-r) % pa, pa) % pa;for(; v; v--) res = res * p % pa;return res;}
};//扩展 Lucas ,负责完成质因数分解和 CRT 合并。
int exLucas(int n, int r, int m, int res = 0) {int tmp = m;for(int p=2; p*p<=tmp; p++) {  //试除法分解质因数。if(tmp % p) continue;int a = 0, pa = 1;for(; tmp%p==0; tmp/=p) a ++, pa *= p;BPP bpp = BPP(p, a);int ri = bpp.C(n, r);int mi = m / pa;res = (res + mi * ri % m * inv(mi, pa)) % m;  // CRT 合并公式。}if(tmp > 1) {BPP bpp = BPP(tmp, 1);int ri = bpp.C(n, r);int mi = m / tmp;res = (res + mi * ri % m * inv(mi, tmp)) % m;}return res;
}signed main() {fastio;cin>>n>>m>>p;cout<<exLucas(n, m, p);return 0;
}

例题

洛谷 P4720 【模板】扩展卢卡斯定理 / exLucas

这是一道模板题。代码见上面的模板代码。


洛谷 P2480 SDOI2010\ 古代猪文

这题其实并没有用到扩展 Lucas,但是它的思路和扩展 Lucas 是相通的。

详细题解戳 [这里]([Luogu P2480 SDOI2010] 古代猪文 题解 - EtherealYz - 博客园) 。


洛谷 P2183 [国家集训队] 礼物

我们对 \(w_i\) 做前缀和,显然答案是 \(\sum_{i=1}^m\binom{n-s_{i-1}}{w_i}\)

容易注意到当 \(s_{m}>n\) 是无解。

主要难点是不保证模数为质数。这就需要用到扩展 Lucas 定理。

下面贴出代码。相比于上面的模板代码,只改动了主函数。

#include<iostream>
#include<vector>
#define fastio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define int long long
#define MIKU 0
using namespace std;int mod, n, m, ans = 1;
int w[10], s[10];int exgcd(int a, int b, int &x, int &y) {if(b == 0) {x = 1; y = 0; return a;}int d = exgcd(b, a%b, y, x);y -= a / b * x;return d;
}int inv(int a, int m) {int x, y;exgcd(a, m, x, y);return (x % m + m) % m;
}struct BPP {int p, a, pa;vector<int> f;BPP(int prime, int power) : p(prime), a(power) {pa = 1;for(int i=1; i<=a; i++) pa *= p;f.resize(pa); f[0] = 1;for(int i=1; i<pa; i++) f[i] = (i % p) ? f[i-1] * i % pa : f[i-1];}int vp(int n, int cnt = 0) {for(; n; n/=p) cnt += n / p;return cnt;}int fac(int n, int res = 1) {bool neg = (p != 2 || a < 3);for(; n>1; n/=p) {if((n / pa) & neg) res = pa - res;res = res * f[n%pa] % pa;}return res;}int C(int n, int r) {int v = vp(n) - vp(r) - vp(n-r);if(v >= a) return 0;int res = fac(n) * inv(fac(r) * fac(n-r) % pa, pa) % pa;for(; v; v--) res = res * p % pa;return res;}
};int exLucas(int n, int r, int m, int res = 0) {int tmp = m;for(int p=2; p*p<=tmp; p++) {if(tmp % p) continue;int a = 0, pa = 1;for(; tmp%p==0; tmp/=p) a ++, pa *= p;BPP bpp = BPP(p, a);int ri = bpp.C(n, r);int mi = m / pa;res = (res + mi * ri % m * inv(mi, pa)) % m;}if(tmp > 1) {BPP bpp = BPP(tmp, 1);int ri = bpp.C(n, r);int mi = m / tmp;res = (res + mi * ri % m * inv(mi, tmp)) % m;}return res;
}signed main() {fastio;cin>>mod>>n>>m;for(int i=1; i<=m; i++) cin>>w[i], s[i] = s[i-1] + w[i];if(s[m] > n) {cout<<"Impossible"; return MIKU;}for(int i=1; i<=m; i++) ans = ans * exLucas(n-s[i-1], w[i], mod) % mod;cout<<ans;return MIKU;
}

后记

虽然扩展 Lucas 看着很复杂,但实际上也就只是一个求组合数的工具。真正的难题,其难点往往也不止在扩展 Lucas 。更重要的是领会扩展 Lucas 背后的数学原理和思想方法。

http://www.zskr.cn/news/1469929.html

相关文章:

  • 炉石传说HsMod终极指南:如何免费解锁完整自定义体验
  • 南宁家政红黑榜:2026年这四家进入口碑决赛圈,冠军表现亮眼 - 教育信息速递
  • python字串切片
  • 2026实测:实在Agent凭什么在国产Agent混战中脱颖而出?深度拆解其“执行力”天花板
  • 基于实时日志解析与模块化架构的炉石传说智能辅助系统实现
  • Sunshine游戏串流:终极指南搭建你的私人云游戏平台
  • Windows 11系统瘦身指南:如何用Win11Debloat一键清理系统臃肿?
  • 深耕制造质量数字化:我实测发现,质检落地瓶颈从来不是人工不够
  • Databricks集成Azure情感分析v3.0实战:高吞吐低延迟生产部署
  • 3个简单步骤:让PDFViewer成为你的网页PDF阅读器终极解决方案
  • Maxwell 怎么查看指定单一时间点结果|瞬态 Transient 专用
  • Windows Cleaner终极指南:3步解决C盘爆红,让电脑重获新生!
  • 2026年高性价比的乘客电梯公司排名 - mypinpai
  • 新手零基础入门:用快马平台生成fofa查询学习工具,直观掌握资产搜索
  • PHP周刊2026W21 | PHP 基金会成立生态安全团队、Laravel 13.9.0 新增 HTML 密码规则属性、Twig 3.25.0 发布、Symfony 8.1 原生 DeepClon…
  • 【HarmonyOS实战】 距离计算:用MapKit一行代码算出两点距离
  • 选购劳动纠纷律师服务,北京炜衡刘纪伟团队如何? - mypinpai
  • Veo 2高速运动拍摄必改的4个底层参数,错过=浪费47%硬件潜力(含v2.3.1固件兼容性警告)
  • 2026徐州家装消费调研深度报道:整装市场需求多元分化,本土标杆企业破解家装选购痛点 - 招财兔数字员工
  • DeepPCB数据集:3步构建高精度PCB缺陷检测AI系统
  • 2026 XCPC 杂题选解
  • 北京劳动纠纷律师费用多少钱 - mypinpai
  • AI工具越强,人类越弱?斯坦福+MIT联合研究揭示创造力断层的3个隐藏拐点(仅开放72小时)
  • 3分钟掌握VideoDownloadHelper:简单高效的网页视频下载插件终极指南 [特殊字符]
  • 2026年华中区域AI推广好用服务商排名 - mypinpai
  • 北斗GNSS水库变形监测系统的应用与技术优势分析
  • AI礼品推荐准确率提升317%的关键:多模态意图识别+实时情感反馈闭环构建
  • stm32中hal库配置st7789中的1.14ISP的tft屏幕
  • 2026苏州防水补漏哪家好?住建实地测评权威榜单TOP5|卫生间免砸砖/阳台屋顶/厨卫漏水维修(6月苏州专项调研) - 苏易修缮
  • 实战演练:基于快马平台从零构建一个迷你版每日编程竞赛系统