又双叒叕来水数论了

今天来学习\(Lucas \:\ \& \:\ Catalan Number\)

两者有着密切的联系(当然还有CRT),所以放在一起学习一下

\(Lucas\)

定义\(\&\)性质

\(Lucas\)定理是用来求 $C_n^m mod :\ p \(的值。
其中\)n\(和\)m\(是非负整数,\)p\(是素数。
一般用于\)m,n\(很大而\)p\(很小,抑或是\)n,m\(不大但是大于\)p$的情况下来求结果。

用处\(\&\)背景

目前我们学过几个用来求组合数的方法

  • 最暴力的杨辉三角,用二维数组解决,复杂度\(O(n^2)\) 效率低下而且由于数组的原因只能处理很小的数据
  • 将组合式\(C_{n+m}^n \% mod\)转换为\(\frac {(m+n)!} {n! m!} \%mod\),由于除法不可以边除边取\(mod\)所以在处理分母的时候选用了费马小定理,运用逆元的乘法解决。

    看分子的话,\((m+n)!\)在遍历的时候若有元素\(i>mod\),那么取\(mod\)之后整个式子的值就会变成0,而\(Lucas\)的出现,就是为了解决这一问题

引入

针对上述第二种情况,我们先来看一道例题

旗木双翼

题目描述

菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。

棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。

_Itachi听说有不少学弟在省选现场AC了D1T1,解决了菲菲和牛牛的问题,但是_Itachi听说有的人认为复杂度玄学,_Itachi并不想难为学弟学妹,他想为大家节约时间做剩下的题,所以将简化版的D1T1带给大家。

_Itachi也在一块n行m列的棋盘上下棋,不幸的是_Itachi只有黑棋,不过幸好只有他一个人玩。现在,_Itachi想知道,一共有多少种可能的棋局(不考虑落子顺序,只考虑棋子位置)。

_Itachi也不会为难学弟学妹们去写高精度,所以只需要告诉_Itachi答案mod 998244353(一个质数)的结果。

输入格式

第一行包括两个整数n,m表示棋盘为n行m列。

输出格式

一个整数表示可能的棋局种数。

样例输入

10 10

样例输出

184756

数据范围与提示

对于 \(20\%\)的数据$n,m \leq10 $

对于 \(30\%\)的数据$n,m\leq20 $

另有 \(20\%\)的数据$n\leq5 $

另有 \(20\%\)的数据$m\leq5 $

对于\(100\%\)的数据$n,m\leq100000 $

solution

可以看到题目当中直接给出了\(mod\)为一个大质数

而且\(m+n\)最大也才二十万,远远小于\(mod\)的数值

所以不会出现取完\(mod\)为\(0\)的情况,所以运用逆元求解即可

\[C_{n+m}^m =\frac {(m+n)!} {n!*m!} \% mod
\]

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std; inline int read(){
int x = 0, w = 1;
char ch = getchar();
for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return x * w;
} const int mod = 998244353; inline int power(int a, int b){
int ans = 1;
while(b){
if(b & 1) ans = (ans % mod) * (a % mod) % mod;
a = (a % mod) * (a % mod) % mod;
b >>= 1;
}
ans %= mod;
return ans;
} signed main(){
int n = read(), m = read();
int tmp = 1;
for(int i = m + n; i >= 1; i--){
tmp *= i;
tmp %= mod;
}
int n1 = 1;
int m1 = 1;
for(int i = 1; i <= n; i++){
n1 *= i;
n1 %= mod;
}
for(int i = 1; i <= m; i++){
m1 *= i;
m1 %= mod;
}
int ans = (tmp % mod) * power(n1 ,mod - 2) % mod * power(m1, mod - 2) % mod;
ans %= mod;
cout << ans << endl;
return 0;
}

如果\(mod\)很小

上述算法显然在求阶乘的时候会变成\(0\)

这就需要\(Lucas\)定理了

性质

性质1

\[Lucas(n,m,p)=cm(n\%p,m\%p)*Lucas(\frac n p,\frac m p,p)
\]

\[Lucas(x,0,p) =1
\]

其中

\[cm(a,b)=a!*(b!*(a-b)!)^{p-2} (mod \:\ p)
\]

\((a-b)!^{p-2}\)为\(a-b\)的逆元,所以可以除一下把式子变成

\[(a!/(a-b)!)*(b!)^{p-2}(mod \:\ p)
\]

性质2

\[C_n^mmod p\equiv C_{n\mod p}^{m\mod p}*C_{\lfloor n/p\rfloor}^{\lfloor m/p\rfloor}mod p
\]

就是一个\(a,b\)可以拆成\(P\)进制下的乘积

证明

目标方程:

\[\dbinom n m\equiv\dbinom {n\%p} {m\%p} \dbinom {n/p}{m/p}(mod p)
\]

显然右侧可以递归处理,我们只需要推左边就好

证明过程:

\[n=sp+q,m=tp+r,(q,r<p)
\]

则目标方程变成

\[\dbinom {sp+q} {tp+r} \equiv \dbinom {s} {t} \dbinom {q} {r} (mod p)
\]

下面利用二项式定理&费马小证明一个引理

\[(1+x)^n \equiv (1+x)^{ps} * (1+x)^q (mod \:\ p)
\]

\[(1+x)^n\equiv[\sum_{t=0}^p \dbinom p i x^i]^s(mod \:\ p)
\]

根据上面的那个性质

\[\equiv (1+x)^q*(1+x^p)^s(mod p)
\]

\[\equiv \sum_{i=0}^s \binom s i x^{pt} * \sum_{j=0}^q \binom q j x^j(mod p)
\]

则有

\[(1+x)^{sp+q} \equiv\sum_{i=0}^s \binom s i x^{pt} * \sum_{j=0}^q\binom q j x^j(mod p)
\]

\[\sum_{k=0}^{sp+q}\binom {sp+q} {k} x^k\equiv\sum_{i=0}^s \binom s i x^{pt} * \sum_{j=0}^q\binom q j x^j(mod p)
\]

左边\(x^{tp+r}\)的系数为\(\binom {sp+q}{tp+r}\)

右边\(x^{tp+r}\)的系数为\(\binom s t\binom q r\)

故系数相等,原命题得证

\[\dbinom {sp+q} {tp+r} \equiv \dbinom {s} {t} \dbinom {q} {r} (mod p)
\]

\[\dbinom n m\equiv\dbinom {n\%p} {m\%p} \dbinom {n/p}{m/p}(mod p)
\]

\[Lucas(n,m,p)=cm(n\%p,m\%p)*Lucas(\frac n p,\frac m p,p)
\]

\[Lucas(x,0,p) =1
\]

(以上证明结束)

实现过程

  • 目标运算结果\(C_n^m \:\ \%p\)
  • 注意输入的\(n,m\)中,可能会出现\(n\%p<m%p\),那么直接输出0即可。因为若\(n<m\),则\(C_n^m=0\)
  • 在求逆元时,运用快速幂求其\(p-2\)次方
  • 如果要大量运用\(Lucas\)定理,建议使用杨辉三角打组合数表或将阶乘以及阶乘逆元打表

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std; inline int read(){
int x = 0, w = 1;
char ch = getchar();
for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
return x * w;
} int n, m, p; inline int power(int a, int b){
int ans = 1;
while(b){
if(b & 1) ans = (ans % p) * (a % p) % p;
a = (a % p) * (a % p) % p;
b >>= 1;
}
ans %= p;
return ans;
} inline int getc(int n, int m){
if(n < m) return 0;
if(m > n - m) m = n - m;
int s1 = 1, s2 = 1;
for(int i = 0; i < m; i++){
s1 = s1 * (n - i) % p;//(n-m)!/n!
s2 = s2 * (i + 1) % p;//m!
}
return s1 * power(s2, p - 2) % p;
} inline int lucas(int n, int m){
if(m == 0) return 1;
return getc(n % p, m % p) * lucas(n / p, m / p) % p;
} signed main(){
int t = read();
while(t--){
n = read(), m = read(), p = read();
cout << lucas(n + m, m) << endl;
}
return 0;
}

Lucas定理 & Catalan Number & 中国剩余定理(CRT)的更多相关文章

  1. 中国剩余定理(CRT) & 扩展中国剩余定理(ExCRT)总结

    中国剩余定理(CRT) & 扩展中国剩余定理(ExCRT)总结 标签:数学方法--数论 阅读体验:https://zybuluo.com/Junlier/note/1300035 前置浅讲 前 ...

  2. 中国剩余定理(CRT)及其扩展(EXCRT)详解

    问题背景   孙子定理是中国古代求解一次同余式方程组的方法.是数论中一个重要定理.又称中国余数定理.一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作<孙子算经>卷下第 ...

  3. 中国剩余定理 CRT

    中国剩余定理 CRT 正常版本CRT 要解的是一个很容易的东西 \[ \begin{aligned} x\equiv a_1(mod\ m_1)\\ x\equiv a_2(mod\ m_2)\\ . ...

  4. bzoj 3782 上学路线 卢卡斯定理 容斥 中国剩余定理 dp

    LINK:上学路线 从(0,0)走到(n,m)每次只能向上或者向右走 有K个点不能走求方案数,对P取模. \(1\leq N,M\leq 10^10 0\leq T\leq 200\) p=10000 ...

  5. 扩展GCD 中国剩余定理(CRT) 乘法逆元模版

    extend_gcd: 已知 a,b (a>=0,b>=0) 求一组解 (x,y) 使得 (x,y)满足 gcd(a,b) = ax+by 以下代码中d = gcd(a,b).顺便求出gc ...

  6. 中国剩余定理(CRT)及其拓展(ExCRT)

    中国剩余定理 CRT 推导 给定\(n\)个同余方程 \[ \left\{ \begin{aligned} x &\equiv a_1 \pmod{m_1} \\ x &\equiv ...

  7. 学习笔记:中国剩余定理(CRT)

    引入 常想起在空间里见过的一些智力题,这个题你见过吗: 一堆苹果,\(3\)个\(3\)个地取剩\(1\)个,\(5\)个\(5\)个地取剩\(1\)个,\(7\)个\(7\)个地取剩\(2\)个,苹 ...

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

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

  9. [bzoj2142]礼物(扩展lucas定理+中国剩余定理)

    题意:n件礼物,送给m个人,每人的礼物数确定,求方案数. 解题关键:由于模数不是质数,所以由唯一分解定理, $\bmod  = p_1^{{k_1}}p_2^{{k_2}}......p_s^{{k_ ...

随机推荐

  1. Linux Systemd 详细介绍: Unit、Unit File、Systemctl、Target

    Systemd 简介 CentOS 7 使用 Systemd 替换了SysV Ubuntu 从 15.04 开始使用 Systemd Systemd 是 Linux 系统工具,用来启动守护进程,已成为 ...

  2. django 报错处理汇总

    运行 manage.py task时 ,makemigrations抛出以下错误, django.db.utils.OperationalError: (1045, "Access deni ...

  3. C# CLosedXML四句代码搞定DataTable数据导出到Excel

    最近用到DataTable导出到Excel,网上看了一下,都不怎么好使,逛了下GitHub一下完美解决了 用到的.net库CLosedXML,这个库用于读取,处理和写入Excel 2007+(.xls ...

  4. Quartz.Net系列(九):Trigger之DailyTimeIntervalScheduleBuilder详解

    1.介绍 中文意义就是每日时间间隔计划生成 2.API讲解 (1)WithInterval.WithIntervalInHours.WithIntervalInMinutes.WithInterval ...

  5. mysql无限级分类

    第一种方案: 使用递归算法,也是使用频率最多的,大部分开源程序也是这么处理,不过一般都只用到四级分类. 这种算法的数据库结构设计最为简单.category表中一个字段id,一个字段fid(父id).这 ...

  6. 使用docker创建rocketMQ容器

    一.rocketMQ安装 (一)安装NameSrv 1.创建nameSrv数据挂载文件夹 mkdir -p /usr/data/rocketMQ/data/namesrv/logs mkdir -p ...

  7. eclipse在debug模式下鼠标移动到变量上不显示值的问题

    在eclipse中调试时,鼠标移动到变量上不显示值,使用ctrl+shift+i,或者通过配置达到目的: Window->Preferences->Java->Editor-> ...

  8. JavaScript笔记- 函数声明和函数表达式(001)

    function 是 Javascript 中的第一类对象,这就意味着函数可以像其他值一样被传递.一个最常见的用法就是将一个匿名函数作为回调函数传递到另外一个异步函数中. 函数声明 ? 1 funct ...

  9. 几个常见CSS错误和解决办法

    1.IE6下,当float存在时,margin双倍的问题 解决方法:加display:inline; 例: #content {    float: left;     width: 500px;   ...

  10. win中mysql安装

    mysql安装: https://www.mysql.com/>官网 下载 [DOWNLOADS]----[MySQL Community Server]社区版---5.6---No thank ...