扩展卢卡斯定理用于求如下式子(其中\(p\)不一定是质数):

\[C_n^m\ mod\ p
\]

我们将这个问题由总体到局部地分为三个层次解决。

层次一:原问题

首先对\(p\)进行质因数分解:

\[p=\prod_i p_i^{k_i}
\]

显然\(p_i^{k_i}\)是两两互质的,所以如果分别求出\(C_n^m\ mod\ p_i^{k_i}\),就可以构造出若干个形如\(C_n^m=a_i\ mod\ p_i^{k_i}\)的方程,然后用中国剩余定理即可求解。

层次二:组合数模质数幂

现在的问题就转化成了求如下式子(其中\(p\)是质数):

\[C_n^m\ mod\ p^k
\]

脑补一下组合数公式\(C_n^m=\frac{n!}{m!\times (n-m)!}\),发现由于\(m!\)和\((n-m)!\)可能包含质因子\(p\),所以不能直接求他们对于\(p^k\)的逆元。此时我们可以将\(n!\)、\(m!\)、\((n-m)!\)中的质因子\(p\)全部提出来,最后再乘回去即可。即变为下式(\(k1\)为\(n!\)中质因子\(p\)的次数,\(k2\)、\(k3\)同理):

\[\frac{\frac{n!} {p^{k1}}}{\frac{m!}{p^{k2}}\times \frac{(n-m)!}{p^{k3}}}\times p^{k1-k2-k3}
\]

\(\frac{m!}{p^{k2}}\)和\(\frac{(n-m)!}{p^{k3}}\)和\(p^k\)是互质的,可以直接求逆元。

层次三:阶乘除去质因子后模质数幂

现在看看如何计算形如下式的式子。

\[\frac{n!}{p^{a}}\ mod\ p^k
\]

先考虑如何计算\(n!\ mod\ p^k\)

举个例子:\(n=22\),\(p=3\),\(k=2\)

把这个写出来:

\(22!=1\times 2\times 3\times 4\times 5\times 6\times 7\times 8\times 9\times 10 \times 11\times 12\times 13\times 14\times 15\times 16\times 17\times 18\times 19 \times 20 \times 21 \times 22\)

把其中所有\(p\)(也就是\(3\))的倍数提取出来,得到:

\(22!=3^7 \times (1\times 2\times 3\times 4\times 5\times 6\times 7)\times(1\times 2\times 4\times 5\times 7\times 8\times 10 \times 11\times 13\times 14\times 16\times 17\times 19 \times 20 \times 22 )\)

可以看出上式分为三个部分:第一个部分是\(3\)的幂,次数是小于等于\(22\)的\(3\)的倍数的个数,即\(\lfloor\frac{n}{p}\rfloor\)

第二个部分是一个阶乘\(7!\),即\(\lfloor\frac{n}{p}\rfloor!\),可以递归解决

第三个部分是\(n!\)中与\(p\)互质的部分的乘积,这一部分具有如下性质:

\(1\times 2\times 4\times 5\times 7\times 8\equiv10 \times 11\times 13\times 14\times 16\times 17\ mod\ p^k\)

在模\(3^2\)的意义下\(10\)和\(1\)同余,\(11\)和\(2\)同余……写成下式就比较显然

(\(t\)是任意正整数)

\[\prod_{i,(i,p)=1}^{p^k}i\equiv\prod_{i,(i,p)=1}^{p^k}(i+tp^k)\ mod\ p^k
\]

\(\prod_{i,(i,p)=1}^{p^k}i\)一共循环了\(\lfloor\frac{n}{p^k}\rfloor\)次,暴力求出\(\prod_{i,(i,p)=1}^{p^k}i\)然后用快速幂求它的\(\lfloor\frac{n}{p^k}\rfloor\)次幂。

最后还要乘上\(19\times 20 \times 22\)(即\(\prod_{i,(i,p)=1}^{n\ mod\ p^k}i\)),显然这一段的长度一定小于\(p^k\),暴力乘上去即可。

如上三部分的乘积就是\(n!\)。最终要求的是\(\frac{n!}{p^{a}}\ mod\ p^k\),分母全部由上述第一部分和第二部分贡献(第三部分和\(p\)互质)。而递归计算第二部分的时候已经除去了第二部分中的因子\(p\),所以最终的答案就是上述第二部分递归返回的结果和第三部分的乘积(与第一部分无关)。

结合代码方便理解:

ll fac(const ll n, const ll p, const ll pk)
{
if (!n)
return 1;
ll ans = 1;
for (int i = 1; i < pk; i++)
if (i % p)
ans = ans * i % pk;
ans = power(ans, n / pk, pk);
for (int i = 1; i <= n % pk; i++)
if (i % p)
ans = ans * i % pk;
return ans * fac(n / p, p, pk) % pk;
}

层次二:组合数模质数幂

回到这个式子

\[\frac{\frac{n!} {p^{k1}}}{\frac{m!}{p^{k2}}\times \frac{(n-m)!}{p^{k3}}}\times p^{k1-k2-k3}
\]

可以很容易地把它转换成代码(注意i要开long long):

ll C(const ll n, const ll m, const ll p, const ll pk)
{
if (n < m)
return 0;
ll f1 = fac(n, p, pk), f2 = fac(m, p, pk), f3 = fac(n - m, p, pk), cnt = 0;
for (ll i = n; i; i /= p)
cnt += i / p;
for (ll i = m; i; i /= p)
cnt -= i / p;
for (ll i = n - m; i; i /= p)
cnt -= i / p;
return f1 * inv(f2, pk) % pk * inv(f3, pk) % pk * power(p, cnt, pk) % pk;
}

层次一:原问题

完整代码(题目:洛谷4720【模板】扩展卢卡斯):

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <climits>
#include <cmath>
using namespace std;
namespace zyt
{
const int N = 1e6;
typedef long long ll;
ll n, m, p;
inline ll power(ll a, ll b, const ll p = LLONG_MAX)
{
ll ans = 1;
while (b)
{
if (b & 1)
ans = ans * a % p;
a = a * a % p;
b >>= 1;
}
return ans;
}
ll fac(const ll n, const ll p, const ll pk)
{
if (!n)
return 1;
ll ans = 1;
for (int i = 1; i < pk; i++)
if (i % p)
ans = ans * i % pk;
ans = power(ans, n / pk, pk);
for (int i = 1; i <= n % pk; i++)
if (i % p)
ans = ans * i % pk;
return ans * fac(n / p, p, pk) % pk;
}
ll exgcd(const ll a, const ll b, ll &x, ll &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
ll xx, yy, g = exgcd(b, a % b, xx, yy);
x = yy;
y = xx - a / b * yy;
return g;
}
ll inv(const ll a, const ll p)
{
ll x, y;
exgcd(a, p, x, y);
return (x % p + p) % p;
}
ll C(const ll n, const ll m, const ll p, const ll pk)
{
if (n < m)
return 0;
ll f1 = fac(n, p, pk), f2 = fac(m, p, pk), f3 = fac(n - m, p, pk), cnt = 0;
for (ll i = n; i; i /= p)
cnt += i / p;
for (ll i = m; i; i /= p)
cnt -= i / p;
for (ll i = n - m; i; i /= p)
cnt -= i / p;
return f1 * inv(f2, pk) % pk * inv(f3, pk) % pk * power(p, cnt, pk) % pk;
}
ll a[N], c[N];
int cnt;
inline ll CRT()
{
ll M = 1, ans = 0;
for (int i = 0; i < cnt; i++)
M *= c[i];
for (int i = 0; i < cnt; i++)
ans = (ans + a[i] * (M / c[i]) % M * inv(M / c[i], c[i]) % M) % M;
return ans;
}
ll exlucas(const ll n, const ll m, ll p)
{
ll tmp = sqrt(p);
for (int i = 2; p > 1 && i <= tmp; i++)
{
ll tmp = 1;
while (p % i == 0)
p /= i, tmp *= i;
if (tmp > 1)
a[cnt] = C(n, m, i, tmp), c[cnt++] = tmp;
}
if (p > 1)
a[cnt] = C(n, m, p, p), c[cnt++] = p;
return CRT();
}
int work()
{
ios::sync_with_stdio(false);
cin >> n >> m >> p;
cout << exlucas(n, m, p);
return 0;
}
}
int main()
{
return zyt::work();
}

【知识总结】扩展卢卡斯定理(exLucas)的更多相关文章

  1. 【学习笔记】扩展卢卡斯定理 exLucas

    引子 求 \[C_n^m\ \text{mod}\ p \] 不保证 \(p\) 是质数. 正文 对于传统的 Lucas 定理,必须要求 \(p\) 是质数才行.若 \(p\) 不一定是质数,则需要扩 ...

  2. 扩展卢卡斯定理(Exlucas)

    题目链接 戳我 前置知识 中国剩余定理(crt)或扩展中国剩余定理(excrt) 乘法逆元 组合数的基本运用 扩展欧几里得(exgcd) 说实话Lucas真的和这个没有什么太大的关系,但是Lucas还 ...

  3. bzoj2142 礼物——扩展卢卡斯定理

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2142 前几天学了扩展卢卡斯定理,今天来磕模板! 这道题式子挺好推的(连我都自己推出来了) , ...

  4. 卢卡斯定理&扩展卢卡斯定理

    卢卡斯定理 求\(C_m^n~mod~p\) 设\(m={a_0}^{p_0}+{a_1}^{p_1}+\cdots+{a_k}^{p_k},n={b_0}^{p_0}+{b_1}^{p_1}+\cd ...

  5. LG4720 【模板】扩展卢卡斯定理

    扩展卢卡斯定理 求 \(C_n^m \bmod{p}\),其中 \(C\) 为组合数. \(1≤m≤n≤10^{18},2≤p≤1000000\) ,不保证 \(p\) 是质数. Fading的题解 ...

  6. 洛谷 P4720 【模板】扩展 / 卢卡斯 模板题

    扩展卢卡斯定理 : https://www.luogu.org/problemnew/show/P4720 卢卡斯定理:https://www.luogu.org/problemnew/show/P3 ...

  7. CRT中国剩余定理 & Lucas卢卡斯定理

    数论_CRT(中国剩余定理)& Lucas (卢卡斯定理) 前言 又是一脸懵逼的一天. 正文 按照道理来说,我们应该先做一个介绍. 中国剩余定理 中国剩余定理,Chinese Remainde ...

  8. P4720【模板】扩展卢卡斯,P2183 礼物

    扩展卢卡斯定理 最近光做模板了 想了解卢卡斯定理的去这里,那题也有我的题解 然而这题和卢卡斯定理并没有太大关系(雾 但是,首先要会的是中国剩余定理和exgcd 卢卡斯定理用于求\(n,m\)大,但模数 ...

  9. [学习笔记]扩展LUCAS定理

    可以先做这个题[SDOI2010]古代猪文 此算法和LUCAS定理没有半毛钱关系. [模板]扩展卢卡斯 不保证P是质数. $C_n^m=\frac{n!}{m!(n-m)!}$ 麻烦的是分母. 如果互 ...

随机推荐

  1. cmake 学习笔记(一) buildsystem

    参见网址: http://www.cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html Introduction 基于CMake的构建 ...

  2. 洛谷 2176 [USACO14FEB]路障Roadblock

    [题意概述] 修改图中任一一条边的边权,使其加倍,问怎样使修改后图中的1~n的最短路最大.输出最短路的增量. [题解] 先跑一遍dijkstra求出1~n的路径长度,记录下经过的边.枚举这些边进行修改 ...

  3. Spring Security核心类关系图

    以有限的脑力记忆无限的Knowledge,多画图,多画图,多画图. 核心类Authentication 和 GrantedAuthority AbstractAuthenticationToken 由 ...

  4. Spring MVC学习总结(8)——Swagger入门详解

    前言 Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件.本文简单介绍了在项目中集成swagger的方法和一些常见问题.如果想深入分析项目源码,了解更多内容,见参考资料. S ...

  5. ansible playbooks loop循环

    在一个task中循环某个操作 1.标准循环 - name: add several users user: name: "{{ item }}" state: present gr ...

  6. 九度oj 题目1473:二进制数

    题目描述: 大家都知道,数据在计算机里中存储是以二进制的形式存储的. 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的. 你能帮 ...

  7. NOIP2015 提高组合集

    NOIP 2015 提高组 合集 D1 T1 神奇的幻方 题目让你干啥你就干啥,让你咋走你就咋走就完事儿了 #include <iostream> #include <cstdio& ...

  8. Ubuntu下使用Sysvinit实现自定义服务(简单研究)

    通过上一篇文章http://www.cnblogs.com/EasonJim/p/7168216.html可以大概了解到Sysvinit的历史. 其实在自定义服务上,使用Sysvinit是最简单的,本 ...

  9. Ubuntu 16.04禁用来宾账号(Guest User)

    1.打开50-ubuntu.conf sudo vim /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf 2.在最后加入 allow-guest=fal ...

  10. 关于static静态块的使用和static list的使用

    public class CorsConfiguration { /** * Wildcard representing <em>all</em> origins, metho ...