一个有趣的问题

题目大意

  给定$n, m, p$,求$C_{n}^{m}$除以$p$后的余数。

Subtask#1  $0\leqslant m\leqslant n \leqslant 2\times 10^{3}$

  直接杨辉恒等式$C_{n}^{m} = C_{n - 1}^{m - 1} + C_{n - 1}^{m}$递推。

  时间复杂度$O(n^{2})$。

Subtask#2  $0\leqslant m\leqslant n \leqslant 2\times 10^{6},p = 10^{9} + 7$

  用式子$C_{n}^{m} = \frac{n!}{m!(n - m)!}$来计算。

  直接算阶乘,用快速幂或扩欧求逆元即可。

  时间复杂度$O(n + \log n)$

Subtask#3 $0\leqslant m\leqslant n \leqslant 10^{9},p\leqslant 10^{6}$且$p$为质数。

  好吧,现在引入正题。

定理1(Lucas定理) 当$p$是一个质数时,$n = k_{1}p + r_{1}, m = k_{2}p + r_{2} \left ( 0\leqslant r_{1},r_{2} < p \right )$,则有$C_{n}^{m} \equiv C_{k_{1}}^{k_{2}}C_{r_{1}}^{r_{2}} \pmod{p}$

  考虑怎么使用它。

  对于每次操作的$r_{1}$和$r_{2}$都是小于$p$的,这个可以直接用Subtask 2的方法来做。

  然后剩下的$C_{k_{1}}^{k_{2}}$就递归处理。

  这很简单,就不给代码了。

  现在来考虑Lucas定理的证明。

  首先你需要一个二项式定理。

定理2(二项式定理) 当$n$是一个非负整数时,则有,$\left (a + b  \right ) ^{n} = \sum_{i = 0} ^{n}C_{n}^{i}a^{i}b^{n - i}$

  证明 考虑使用数学归纳法来证明。

  当$n = 1$的时候,显然成立。

  假设当$n = k - 1$的时候成立,考虑当$n = k$的时候

$\left (a + b  \right )^{k} = \left ( a + b \right )^{k - 1}\left ( a + b \right )\\=\left ( \sum_{i = 0}^{k - 1}C_{k-1}^{i}a^{i}b^{k - i - 1} \right )(a + b)\\=\sum_{i = 1}^{k}C_{k - 1}^{i - 1}a^{i}b^{k - i} + \sum_{i = 0}^{k - 1}C_{k - 1}^{i}a^{i}b^{k - i}\\=C_{k - 1}^{k - 1}a^{n} + \sum_{i = 1}^{k - 1}\left ( C_{k - 1}^{i - 1} + C_{k - 1}^{i} \right )a^{i}b^{k - i} + C_{k - 1}^{0}b^{n}\\=\sum_{i = 0}^{k} C_{k}^{i} a^{i}b^{k - i}$

  因此,定理得证。

定理3 若$p$为质数,若有整数$k$满足$1\leqslant k < p$,则有$p \mid C_{p}^{k}$

  根据式子$C_{p}^{k} = \frac{p!}{k!\left( p - k \right)!}$和素数的定义易证。

推论4 若$p$为质数,则$\left ( 1 + x\right )^{p}\equiv 1 + x^{p} \pmod{p}$。

  根据定理3和二项式定理易证。

  现在来考虑Lucas定理的证明。

  Lucas定理的证明

$\left ( 1 + x\right )^{n}\\\equiv \left ( 1 + x \right )^{k_{1}p}\left( 1 + x\right )^{r_{1}} \\\equiv \left ( 1 + x^{p} \right )^{k_{1}}\left( 1 + x\right )^{r_{1}}\\\equiv \left ( \sum_{i = 0}^{k_{1}}C_{k_{1}}^{i}x^{pi} \right )\left (   \sum_{i = 0}^{r_{1}}C_{r_{1}}^{i}x^{i} \right )\\\equiv \sum_{i = 0}^{k_{1}}\sum_{j = 0}^{r_{1}}C_{k_{1}}^{i}C_{r_{1}}^{j}x^{pi + j} \pmod{p}$

  然后考虑$x^{m}$的系数,同余式左边显然是$C_{n}^{m}$,右边是$C_{k_{1}}^{k_{2}}C_{r_{1}}^{r_{2}}$,因此定理得证。

扩展Lucas算法

  继续回到上面的问题。

Subtask#4 $0 \leqslant m \leqslant n \leqslant 10^{7},p\leqslant 10^{9}$

  继续考虑组合数的计算公式$C_{n}^{m} = \frac{n!}{m!(n - m)!}$,考虑将$n!, m!, (n - m)!$质因数分解,这样直接就可以指数相加减,最后乘起来就好了。

  考虑怎么数$n!$中质因子$p$的指数。

  我们知道$n! = 1\times 2\times 3 \times \cdots \times n$,其中是$p$的倍数的是$p, 2p, 3p, \cdots$,考虑将这些$p$除掉,然后它会变成$1, 2, 3, \cdots, \left \lfloor \frac{n}{p} \right \rfloor$。这是一个规模更小的子问题,递归处理即可。时间复杂度$O(log_{p}n)$。

  可以用线性筛先预处理$10^{7}$以内的素数,然后考虑$n!$的时候对小于$n$的每个素数去跑一次这个算法,因为$n$以内约有$\frac{n}{\log n}$个素数,所以时间复杂度为$O(n)$。

Subtask#5 $0 \leqslant m \leqslant n \leqslant 10^{9},p\leqslant 10^{9}$,若将$p$质因数分解得到:$p = p_{1}^{a_{1}}\cdots p_{k}^{a_{k}}$,,则对于任意$i = 1, 2, 3,\cdots, k$都有$p_{i}^{a_{i}}\leqslant 10^{5}$

  数据范围怎么越来越不友好了?话说后面那一大句又是什么奇怪的性质?

  继续考虑直接暴力,上面通过质因数分解的方法已经不可行。

  以上NB的方法是阶乘加逆元对吧?因为模数不一定是质数,所以和$n$之类的不一定互质,也就是说逆元可能不存在。

  设答案为$x$,那么可以将原问题拆成$k$个子问题:

$\left\{\begin{matrix}x\equiv C_{n}^{m} \pmod{p_{1}^{a_{1}}} \\ x\equiv C_{n}^{m} \pmod{p_{2}^{a_{2}}}\\ \vdots \\  x\equiv C_{n}^{m} \pmod{p_{k}^{a_{k}}}\end{matrix}\right.$

  这有什么卵用?

  假如能够把$p_{i}$抽出去单独计算,剩下就可以"快速阶乘",因为此时和模数互质,所以可以直接用逆元在模意义下做除法。最后用中国剩余定理合并。

  把$p_{i}$抽出去可以用上面的方法进行计算。

  现在考虑如何快速计算模$p_{i}^{a_{i}}$下,被抽取所有有关$p_{i}$的因子的$n!$。

  我们直接暴力枚举$1$ ~ $p_{i}^{a_{i}}$中间的数,然后暴力将不是$p_{i}$的倍数的数乘起来,对于中间在模意义下是一样的,快速幂乘一乘,最后末尾的暴力做一下。

  然后把是$p_{i}$的倍数的除掉一个$p_{i}$,这样问题的规模会变小,递归处理即可

  举个例子有助于说明:

假设$p_{i} = 3, a_{i} = 2, n = 19$,那么要计算的是:$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$

显然它同余于$\left (1 \times 2\times 4 \times 5 \times 7 \times 8   \right )\times \left (1 \times 2\times 4 \times 5 \times 7 \times 8   \right ) \times 1 \times 3\times \left ( 1 \times 2\times 3 \times 4 \times 5\times 6 \right )$

前面的用快速幂,末尾余下的暴力算,后面的递归处理。

  时间复杂度O(能过)

  没错,这就是扩展Lucas,我很好奇它和Lucas有什么关系。这明明就是依赖特殊性质的暴力qwq。

  最后附上代码。

Code

 /**
* bzoj
* Problem#2142
* Accepted
* Time: 372ms
* Memory: 1292k
*/
#include <bits/stdc++.h>
#ifndef WIN32
#define Auto "%lld"
#else
#define Auto "%I64d"
#endif
using namespace std;
typedef bool boolean; #define ll long long int qpow(int a, int pos, int m) {
int pa = a, rt = ;
for ( ; pos; pos >>= , pa = pa * 1ll * pa % m)
if (pos & )
rt = rt * 1ll * pa % m;
return rt;
} void exgcd(ll a, ll b, ll& d, ll& x, ll& y) {
if (!b)
d = a, x = , y = ;
else {
exgcd(b, a % b, d, y, x);
y -= (a / b) * x;
}
} ll inv(ll a, ll n) {
ll d, x, y;
exgcd(a, n, d, x, y);
return (x < ) ? (x + n) : (x);
} ll p;
int n, m, k;
int top = ;
int w[];
int fac[];
int val[]; inline void init() {
scanf(Auto"%d%d", &p, &n, &m);
ll ws = ;
for (int i = ; i <= m; i++) {
scanf("%d", w + i);
ws += w[i];
}
if (ws > n) {
puts("Impossible");
exit();
}
k = ws;
} void getFactors(ll x) {
for (int i = ; x != ; i++) {
if (!(x % i)) {
fac[++top] = i, val[top] = ;
while (!(x % i)) val[top] *= i, x /= i;
}
}
} int getPos(int n, int p) {
int rt = ;
while (n) rt += (n /= p);
return rt;
} int calc(int n, int p, int pr) {
if (!n) return ;
int rt = ;
for (int i = ; i < pr; i++)
if (i % p)
rt = rt * 1ll * i % pr;
rt = qpow(rt, n / pr, pr);
for (int i = ; i <= n % pr; i++)
if (i % p)
rt = rt * 1ll * i % pr;
return rt * 1ll * calc(n / p, p, pr) % pr;
} ll exLucas(int n, int m, ll ap) {
ll rt = , C;
for (int i = , t1, t2, t3, r1, r2, r3, p, pr; i <= top; i++) {
p = fac[i], pr = val[i];
r1 = getPos(n, p), r2 = getPos(m, p), r3 = getPos(n - m, p);
t1 = calc(n, p, pr), t2 = calc(m, p, pr), t3 = calc(n - m, p, pr);
C = ((t1 * inv(t2, pr) % pr) * inv(t3, pr) % pr * 1ll * qpow(p, r1 - r2 - r3, pr)) % pr;
rt = (rt + (((ap / pr) * inv(ap / pr, pr) % ap) * C % ap)) % ap;
}
return rt;
} inline void solve() {
getFactors(p);
ll C = exLucas(n, k, p);
for (int i = ; i <= m; i++) {
C = C * exLucas(k, w[i], p) % p;
k -= w[i];
}
printf(Auto, C);
} int main() {
init();
solve();
return ;
}

Lucas定理学习笔记的更多相关文章

  1. Lucas定理学习笔记(没有ex_lucas)

    题目链接\(Click\) \(Here\) \(ex\_lucas\)实在是不能学的东西...简单学了一下\(Lucas\)然后打算就这样鸽着了\(QwQ\)(奶一口不可能考) 没什么复杂的,证明的 ...

  2. lucas 定理学习

    大致意思就是求组合数C(n , m) % p的值, p为一个偶数 可以将组合数的n 和 m都理解为 p 进制的表示 n  = ak*p^k + a(k-1)*p^(k-1) + ... + a1*p ...

  3. Lucas定理学习小记

    (1)Lucas定理:p为素数,则有: (2)证明: n=(ak...a2,a1,a0)p = (ak...a2,a1)p*p + a0 =  [n/p]*p+a0,m=[m/p]*p+b0其次,我们 ...

  4. Lucas定理学习(进阶中)

    (1)Lucas定理:p为素数,则有: (2)证明: n=(ak...a2,a1,a0)p = (ak...a2,a1)p*p + a0 =  [n/p]*p+a0,m=[m/p]*p+b0其次,我们 ...

  5. lucas定理学习

    Lucas定理是用来求 c(n,m) mod p,p为素数的值. 表达式: C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p 当我们遇到求一个N,M很大的组合数的时候,递推法就显得很耗 ...

  6. Burnside引理与Polya定理 学习笔记

    原文链接www.cnblogs.com/zhouzhendong/p/Burnside-Polya.html 问题模型 有一个长度为 $n$ 的序列,序列中的每一个元素有 $m$ 种取值. 如果两个序 ...

  7. Master定理学习笔记

    前言 \(Master\)定理,又称主定理,用于程序的时间复杂度计算,核心思想是分治,近几年\(Noip\)常考时间复杂度的题目,都需要主定理进行运算. 前置 我们常见的程序时间复杂度有: \(O(n ...

  8. Matrix_tree Theorem 矩阵树定理学习笔记

    Matrix_tree Theorem: 给定一个无向图, 定义矩阵A A[i][j] = - (<i, j>之间的边数) A[i][i] = 点i的度数 其生成树的个数等于 A的任意n ...

  9. 生成树计数 Matrix-Tree 定理 学习笔记

    一直都知道要用Matrix-Tree定理来解决生成树计数问题,但是拖到今天才来学.博主数学不好也只能跟着各位大佬博客学一下它的应用以及会做题,证明实在是不会. 推荐博客: https://www.cn ...

随机推荐

  1. vue中输入框聚焦,自动跳转下一个输入框

    比如 点击入库,el-dialog弹出来,然后自动聚焦第一个输入框,当输入框有值的时候,自动跳转下一个输入框 这个需求 直接上菜: this.$refs.lbj.focus()其实直接这么写也可以,但 ...

  2. webpack使用四

    鼎鼎大名的Loaders登场了! Loaders是webpack提供的最激动人心的功能之一了.通过使用不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分 ...

  3. LeetCode38.报数

    报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数.其前五项如下: 1. 1 2. 11 3. 21 4. 1211 5. 111221 1 被读作  "one 1" ...

  4. EL的隐含对象(一)【页面上下文对象】

    页面上下文对象为pageContext,用于访问JSP内置对象(例如:request.response.out.session.exception.page等)和ServletContext.在获取到 ...

  5. asp.net webapi 404/或无效控制器/或无效请求 截取处理统一输出格式

    public static class PreRouteHandler     {         public static void HttpPreRoute(this HttpConfigura ...

  6. sudo安装某一文件报错:E: 无法获得锁 /var/lib/dpkg/lock - open (11: 资源暂时不可用) E: 无法锁定管理目录(/var/lib/dpkg/),是否有其他进程正占用它?

    报错原因:资源被占用 解决方法: sudo rm /var/cache/apt/archives/lock sudo rm /var/lib/dpkg/lock

  7. 深入解析Java反射(1) - 基础

    深入解析Java反射(1) - 基础 最近正筹备Samsara框架的开发,而其中的IOC部分非常依靠反射,因此趁这个机会来总结一下关于Java反射的一些知识.本篇为基本篇,基于JDK 1.8. 一.回 ...

  8. ABC3

    Sql Server http://www.cnblogs.com/sunxi/p/4600152.html http://blog.csdn.net/dmz1981/article/details/ ...

  9. linux命令-查找所有文件中包含某个字符串

    查找目录下的所有文件中是否含有某个字符串 find .|xargs grep -ri "IBM" 查找目录下的所有文件中是否含有某个字符串,并且只打印出文件名 find .|xar ...

  10. Python - 2. Built-in Collection Data Types

    From: http://interactivepython.org/courselib/static/pythonds/Introduction/GettingStartedwithData.htm ...