又双叒叕来水数论了
今天来学习\(Lucas \:\ \& \:\ Catalan Number\)
二者有着密切的联系(固然还有CRT),因此放在一块儿学习一下ios
卡特兰数(Catalan Number)又称卡塔兰数,是组合数学中一个常常出如今各类计数问题中的数列
\(1,2,5,14,42,132,429,1430,4862,16796,58786,208010,742900\)
啊这
其实跟\(Lucas\)没有什么太大的联系
只有其中一个求\(Catalan Number\)的公式可能会用到\(Lucas\)定理算法
上面提到说其中一个公式,也就是说求\(Catalan Number\)有不少方法数组
通常用来解决一些现实问题,很复杂的题目被看破以后直接用公式算出来便可
举个例子,合法的入栈出栈序列有多少种就是卡特兰数。咱们能够把0当作入栈操做,1当作出栈操做,即0的累计个数不小于1的排列有多少种。
还有不少其余的问题都是卡特兰数,如括号匹配,二叉树的个数,有序树的个数,多边形分红三角形的个数等。学习
n+1 个叶子节点的二叉树的数量
ui
n*n的方格地图中,从一个角到另一个角,不跨越对角线的路径数
spa
n+2条边的多边形,能被分割成三角形的方案数
code
来看一道例题作入门吧blog
众所周知,在中国古代算筹中,红为正,黑为负……
给定一个\(1*(2n)\)的矩阵,现让你自由地放入红色算筹和黑色算筹,使矩阵平衡[即对于全部的\(i(1<=i<=2n)\),使第\(1-i\)格中红色算筹个数大于等于黑色算筹]
问有多少种方案知足矩阵平衡。递归
正整数 nget
方案数t对100取模
2
2
红 黑 红 黑
红 红 黑 黑
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int main(){ long long ctl[110],i,j,k,n; memset(ctl,0,sizeof(ctl)); ctl[0]=ctl[1]=1; ctl[2]=2; scanf("%lld",&n); for(i=3;i<=n;++i) for(j=0;j<i;++j){ ctl[i]+=ctl[j]*ctl[i-j-1]; ctl[i]%=100; } printf("%lld\n",ctl[n]); return 0; }
这里只给出了第一个式子的应用,更多的请自行查找题进行思惟练习。
\(Lucas\)定理是用来求 $ C_n^m mod p $ 的值。
其中\(n\)和\(m\)是非负整数,\(p\)是素数。
通常用于\(m,n\)很大而\(p\)很小,抑或是\(n,m\)不大可是大于\(p\)的状况下来求结果。
目前咱们学过几个用来求组合数的方法
针对上述第二种状况,咱们先来看一道例题
菲菲和牛牛在一块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 $
能够看到题目当中直接给出了\(mod\)为一个大质数
并且\(m+n\)最大也才二十万,远远小于\(mod\)的数值
因此不会出现取完\(mod\)为\(0\)的状况,因此运用逆元求解便可
#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\)定理了
其中
$ :\ :\ :\ :\ :\ :\ :\ :\ :\ $ \((a-b)!^{p-2}\)为\(a-b\)的逆元,因此能够除一下把式子变成
就是一个\(a,b\)能够拆成\(P\)进制下的乘积
目标方程:
显然右侧能够递归处理,咱们只须要推左边就好
证实过程:
设
则目标方程变成
下面利用二项式定理&费马小证实一个引理
根据上面的那个性质
则有
左边\(x^{tp+r}\)的系数为\(\binom {sp+q}{tp+r}\)
右边\(x^{tp+r}\)的系数为\(\binom s t\binom q r\)
故系数相等,原命题得证
即
即
(以上证实结束)
#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; }