又双叒叕来水数论了

今天来学习\(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. Jmeter Response Data 乱码问题解决方法

    1. 进入jmeter\apache-jmeter-4.0\bin,打开jmeter.properties 2. 搜索“sampleresult.default.encoding” 设置sampler ...

  2. 十万同时在线用户,需要多少内存?——Newbe.Claptrap 框架水平扩展实验

    Newbe.Claptrap 项目是笔者正在构建以反应式.Actor模式和事件溯源为理论基础的一套服务端开发框架.本篇我们将来了解一下框架在水平扩展方面的能力. 前情提要 时隔许久,今日我们再次见面. ...

  3. .Net Core 中GC的工作原理

    前言 .NET 中GC管理你服务的内存分配和释放,GC是运行公共语言运行时(CLR Common Language Runtime)中,GC可以帮助开发人员有效的分配内存和和释放内存,大多数情况下是不 ...

  4. skywalking的插件管理agent管理

    转载自博客:https://my.oschina.net/eacdy/blog/4304312/print Java Agent是插件化.可插拔的.Skywalking的插件分为三种: 引导插件:在a ...

  5. es性能调优---写优化操作

    ES 的默认配置,是综合了数据可靠性.写入速度.搜索实时性等因素.实际使用时,我们需要根据公司要求,进行偏向性的优化. 写优化 假设我们的应用场景要求是,每秒 300 万的写入速度,每条 500 字节 ...

  6. 计算机网络之tcp与udp的区别

    1.基于连接与无连接: 2.对系统资源的要求(TCP较多,UDP少):3.UDP程序结构较简单:4.流模式与数据报模式 :5.TCP保证数据正确性,可靠稳定,UDP可能丢包:6.TCP保证数据顺序,U ...

  7. matlab批量处理数据的方法

    问题描述: 有多个.mat格式数据(本文数据名称:‘buf_026.mat’),要抽取其中的数据进行运算,结果返回到数组/xlsx等 关键字:num2str/ xlsxwrite/ eval/ 元胞数 ...

  8. MFC 添加C++类,别的类不通过C++类的定义的对象就可以直接调用C++类里面的成员函数;

    MFC 添加C++类,不用定义C++类的对象,别的类不通过C++类的定义的对象就可以直接调用C++类里面的成员函数: 1先在mfc程序中添加普通类CProdata,然后删除头文件Prodata.h里面 ...

  9. 《Java核心技术(卷1)》笔记:第8章 泛型程序设计

    (P 327)"菱形"语法: ArrayList<String> files = new ArrayList<>(); // Java 9 扩展了菱形语法的 ...

  10. 用JS实现改变文本框的只读属性

    <input id="aaa" readonly><input id="bbb" readonly> <script>doc ...