【算法讲11:卡特兰数】默慈金数 | 那罗延数 | 施罗德数

⌈ \lceil 卡特兰数 ⌋ \rfloor Catalan Number

引入

  • 鸡蛋饼 | 洛谷 P1976
    一个圆上有 2 × N 2\times N 2×N 个不同的点。用 N N N 条线段将他们两两相连
    要求:每个点都只能被一条线段相连,且线段都不相交。
    求方案数取模一个质数。 N ≤ 2999 N\le 2999 N2999

思考

  • 如果我们用 d p dp dp 去做,肯定会想到一个 O ( N 2 ) O(N^2) O(N2) 的方法,貌似可以过该题。
    d p ( i ) dp(i) dp(i) 表示,有 2 i 2i 2i 个点,满足要求的方案数。
    我们把这 2 i 2i 2i 个点按顺时针编号排好。
    然后,我们以第一个点做基准,考虑该点和哪个点连线,对答案有多少方案数的贡献
    在这里插入图片描述
    容易想到, 1 1 1 必须和 偶数号点相连 ,不然就会出现一侧只剩下奇数个点,无法满足要求。
    假设它和 k k k 号点连接,那么一侧剩下 k − 2 k-2 k2 个点,一侧剩下 2 i − k 2i-k 2ik 个点。
    这就产生了两个子问题。子问题和原问题只有规模的变化,是等价的。
    因为只能选择偶数点,且和 d p dp dp 定义吻合,我们按一对一对来计算绘图:
    在这里插入图片描述
    一侧分为 k k k 对点,分的方案数为 d p ( k ) dp(k) dp(k)
    另一侧分为 i − k − 1 i-k-1 ik1对点,分的方案数为 d p ( i − k − 1 ) dp(i-k-1) dp(ik1)
    按照乘法原则,我们把他们乘起来就是 d p ( i ) dp(i) dp(i) 的一部分。
    然后枚举所有划分情况,即可得出:
    d p ( i ) = { 1 i = 0 ∑ n j = 1 d p ( j − 1 ) d p ( i − j ) i ≥ 1 dp(i)= \begin{cases} 1&i=0\\ \underset{j=1}{\overset{n}{\sum}}dp(j-1)dp(i-j)&i\ge1 \end{cases} dp(i)=1j=1ndp(j1)dp(ij)i=0i1

⌈ \lceil 卡特兰数 ⌋ \rfloor 的性质

  • 于是,我们得到了卡特兰数
    我喜欢记作 C a t ( i ) Cat(i) Cat(i),也有记作 C a t a l a n ( i ) Catalan(i) Catalan(i) C i C_i Ci h i h_i hi 的。
    根据百度百科 [1],我们得到了卡特兰数的一些性质:
  1. 定义式:
    C a t ( i ) = { 1 i = 0 ∑ n j = 1 C a t ( j − 1 ) C a t ( i − j ) i ≥ 1 Cat(i)= \begin{cases} 1&i=0\\ \underset{j=1}{\overset{n}{\sum}}Cat(j-1)Cat(i-j)&i\ge1 \end{cases} Cat(i)=1j=1nCat(j1)Cat(ij)i=0i1
  2. 递推式:
    C a t ( n ) = C a t ( n − 1 ) 4 n − 2 n + 1 Cat(n)=Cat(n-1)\cfrac{4n-2}{n+1} Cat(n)=Cat(n1)n+14n2
  3. 关系式:
    C a t ( n ) = C 2 n n n + 1 = C 2 n n − C 2 n n − 1 Cat(n)=\cfrac{C_{2n}^n}{n+1}=C_{2n}^n-C_{2n}^{n-1} Cat(n)=n+1C2nn=C2nnC2nn1
    最残酷的是,这几个都要记住,每个式子都有自己的优点和缺点…
    神奇的是,卡特兰数在一些看似不同的题目中均有出现。

⌈ \lceil 卡特兰数 ⌋ \rfloor 的应用

图片均出自wikipedia

  • 常规分析法
    就像上面引入的问题一样,每一步可以把大问题划分成两个小问题,然后乘起来求和。
    【鸡蛋饼】 | 洛谷 P1976
    【楼梯搭建】 | 洛谷 P2532
    【凸多边形的三角划分问题】
    【矩阵连乘方案数】
    N N N个节点组成二叉树的方案数】
  • 非常规分析法
    你每次可以向右或向上移动一格,且不能跨过对角线,从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , n ) (n,n) (n,n)的方案数
    在这里插入图片描述
    抽象地,如果向右移动一格记作 + 1 +1 +1,向上移动一格记作 − 1 -1 1,那么卡特兰数还可以表示为:
    每个元素为 ± 1 \pm1 ±1,且每一个前缀和都非负,且整个序列的和为 0 0 0 的长度为 n n n 的序列的个数。
    .
    【矩阵】 | 洛谷 P1722
    红色看做 + 1 +1 +1 黑色看做 − 1 -1 1,满足定义。
    .
    【栈】 | 洛谷 P1044
    栈的进入和进出相当于 + 1 / − 1 +1/-1 +1/1,前缀和非负表示栈内元素数非负,整个序列和为 0 0 0 表示所有元素都进、出栈完成。
    .
    【球迷购票】 | 洛谷 P1754
    50 ¥ 50¥ 50 相当于 + 1 +1 +1 100 ¥ 100¥ 100 要找零相当于 − 1 -1 1,和定义相符。
    `
    n n n对括号的合法匹配个数】
    ( \color{cyan}( ( 看做 + 1 +1 +1 ) \color{cyan}) ) 看做 − 1 -1 1,和定义相符。
    .
    【有趣的序列】 | 洛谷 P3200
    这个问题,等价于:给定 2 × n 2\times n 2×n的矩阵,矩阵中的所有元素构成一个 1 ∼ 2 n 1\sim 2n 12n的全排列
    要求矩阵下边的元素大于上边的,右边的元素大于左边的
    求方案数。(这是什么?这是2020年蓝桥杯省赛的原题呀!)
    非常规思考法:我们依次 1 ∼ 2 n 1\sim 2n 12n 摆放,每次选择摆放上行或者下行。
    要求上行个数每时每刻都要大于等于下行个数,那就设上行放一个元素即为 + 1 +1 +1,下行放为 − 1 -1 1
    然后又又又符合定义了,答案就是 C a t ( n ) Cat(n) Cat(n)
    但是! 这题 n n n 比较大,模数又不是质数,我们目前阶段只能使用 3关系式 做(据说可以分治FFT做啥的?肯定我不懂了)。
    答案就是 ∏ i = n + 2 2 n i ∏ i = 1 n i \cfrac{\prod_{i=n+2}^{2n}i}{\prod_{i=1}^ni} i=1nii=n+22ni
    还是不好办,我们使用 欧拉筛 求出每个数的最小质因数,然后把每个数都分解为一个质数和另一个因数。于是答案最终会变成一些质数的乘积,因为每一个分母都会被某个分子给约掉。
const int MAX = 2e6+50;

ll qpow(ll a,ll n,ll p){a%=p;ll res = 1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}

int ep[MAX],mp[MAX];
int vis[MAX],prime[MAX];
int cnt;
void shai(int n){
    mp[1] = 1;
    for(int i = 2; i <= n;++i){
        if(!vis[i]){
            prime[++cnt] = i;
            mp[i] = i;
        }
        for(int j = 1;j <= cnt && i * prime[j] <= n;++j){
            vis[i * prime[j]] = 1;
            mp[i * prime[j]] = prime[j];
            if(i % prime[j] == 0)break;
        }
    }
}
int main()
{
    int n,p;
    cin >> n >> p;
    shai(n*2);
    for(int i = 1;i <= n;++i)ep[i] = -1;
    for(int i = n + 2;i <= 2 * n;++i)ep[i] = 1;
    for(int i = n * 2;i > 1;--i){
        if(mp[i] < i){
            ep[mp[i]] += ep[i];
            ep[i/mp[i]] += ep[i];
        }
    }
    ll res = 1;
    for(int i = 2;i <= n * 2;++i){
        if(mp[i] == i){
            res = res * qpow(i,ep[i],p) % p;
        }
    }
    cout << res;
    return 0;
}

⌈ \lceil 默慈金数 ⌋ \rfloor Motzkin Number

性质

  • 记作 M i M_i Mi
  1. 定义式:
    M i = { 1 i = 1 2 i = 2 M i − 1 + ∑ i − 2 j = 0 M j M i − j − 2 i ≥ 3 M_i= \begin{cases} 1&i=1\\ 2&i=2\\ M_{i-1}+\underset{j=0}{\overset{i-2}{\sum}}M_{j}M_{i-j-2}&i\ge3 \end{cases} Mi=12Mi1+j=0i2MjMij2i=1i=2i3
  2. 递推式:
    M n = ( 2 n + 1 ) M n − 1 + ( 3 n − 3 ) M n − 2 n + 2 M_n=\cfrac{(2n+1)M_{n-1}+(3n-3)M_{n-2}}{n+2} Mn=n+2(2n+1)Mn1+(3n3)Mn2
  3. 关系式: 式子得到方法见下 [ 3 ]
    M n = ∑ i = 0 ⌊ n 2 ⌋ C n 2 i C a t ( i ) M_n=\sum_{i=0}^{\lfloor\frac{n}{2}\rfloor}C_n^{2i}Cat(i) Mn=i=02nCn2iCat(i)
    可以看到定义式和关系式都表示默慈金数和卡特兰数有某种联系
    咋连一个定义都没有?因为在数学的各分支中,默慈金数至少有十四个彼此不同的展现存在[2]

应用

  • 【弦划分】
    一个圆上有 N N N 个不同的点。
    画出彼此不相交的弦的方案数,即为 M N M_N MN
    在这里插入图片描述
  • 【Delta Wave】 | HDU 3723
    在这里插入图片描述
  • ( 0 , 0 ) (0,0) (0,0) ( N , 0 ) (N,0) (N,0),你每次可以向右上一格/向右下一格/向正右一格,且不能低于 y y y
    方案数就是 M N M_N MN
    可以用关系式推导:假设有 x x x 条上升线段,则必有 x x x 条下降线段。
    我们首先从 N N N 步中选出 2 x 2x 2x 步用于上升与下降,其余步数都是平右。方案数 C N 2 i C_{N}^{2i} CN2i
    接下来,如果上升表示 + 1 +1 +1,下降表示 − 1 -1 1,要求所有前缀和都非负,且总和为 0 0 0
    这样满足 卡特兰数 的性质,故方案数为 C a t ( i ) Cat(i) Cat(i)
    然后枚举所有可能性累加即可。[ 3 ]
  • 【计算】 | 51nod 1556
    长度为 N N N 的序列,每个元素都为正整数。
    第一个元素为 1 1 1,相邻两个数的差不超过 1 1 1。求这样的序列的方案数。
    可以想到,默慈金数的要求:相邻差为 1 1 1 满足,非负要求满足,但是头和尾相同要求没有满足
    我们设 d p ( i ) dp(i) dp(i) 表示满足的答案, M i M_i Mi 表示默慈金数。
    容易想到, d p ( i ) = 3 d p ( i − 1 ) dp(i)=3dp(i-1) dp(i)=3dp(i1)再减去一些非法个数。
    在这里插入图片描述
    乘以三是因为每一步可以向三个方向选择。(蓝箭头)
    非法个数是什么?就是第 i − 1 i-1 i1 步,纵坐标正好为 1 1 1 的方案数。(红箭头为非法方案,即 M i − 2 M_{i-2} Mi2)
    得到了: d p ( i ) = 3 d p ( i − 1 ) − M i − 2 dp(i)=3dp(i-1)-M_{i-2} dp(i)=3dp(i1)Mi2
ll M[MAX];
ll dp[MAX];
int main()
{
    int n;
    cin >> n;
    M[1] = 1;
    M[2] = 2;
    dp[1] = 1;
    dp[2] = 2;
    for(int i = 3;i <= n;++i){
        dp[i] = (3 * dp[i-1] - M[i-2]) % MOD;
        M[i] = M[i-1] * (2*i+1) % MOD + (3*i-3) * M[i-2] % MOD;
        M[i] = M[i] * inv(i + 2) % MOD;
    }
    dp[n] = (dp[n] + MOD) % MOD;
    cout << dp[n];
    return 0;
}

⌈ \lceil 那罗延数 ⌋ \rfloor Narayana number

性质

  • 记作 N ( n , k ) N(n,k) N(n,k) [3]
  1. 定义式:
    N ( n , k ) = 1 n C n k C n k − 1 N(n,k)=\frac{1}{n}C_n^kC_n^{k-1} N(n,k)=n1CnkCnk1
  2. 关系式:
    ∑ i = 1 n N ( n , i ) = C a t ( n ) \sum_{i=1}^nN(n,i)=Cat(n) i=1nN(n,i)=Cat(n)
    那罗延数和卡特兰数的关系可以从关系式中看到。
    看一下这张图:来源 [ 3 ]
    在这里插入图片描述
    可以看到, N ( n , k ) N(n,k) N(n,k) 表示你每次只能向右上一格/向右下一格,从 ( 0 , 0 ) (0,0) (0,0) ( 2 N , 0 ) (2N,0) (2N,0),满足纵坐标非负要求,且 k k k 个的方案数。
    所有的合法方案数,如果坐标系转九十度,等价为:
    每次可以向右走一格/向上走一格, ( 0 , 0 ) (0,0) (0,0) ( n , n ) (n,n) (n,n) 不越过对角线的方案数
    这就得到了该关系式

应用

  • 【括号匹配】
    n n n 对括号,共有 k k k ( \color{cyan}( ( ) \color{cyan}) ) 相邻的合法括号序列方案数为 N ( n , k ) N(n,k) N(n,k)

⌈ \lceil 施罗德数 ⌋ \rfloor Schröder Number

定义

  • 施罗德数也叫大施罗德数,也叫超级卡特兰数。记作 S n S_n Sn
  1. 定义式:
    S n = { 1 n = 0 或 n = 1 S n − 1 + ∑ i = 0 n − 1 S i S n − k − 1 n ≥ 2 S_n= \begin{cases} 1&n=0或n=1\\ S_{n-1}+\sum_{i=0}^{n-1}S_iS_{n-k-1}&n\ge2 \end{cases} Sn={1Sn1+i=0n1SiSnk1n=0n=1n2
  2. 递推式:
    S n = ( 6 n − 3 ) S n − 1 − ( n − 2 ) S n − 2 n + 1 S_n=\cfrac{(6n-3)S_{n-1}-(n-2)S_{n-2}}{n+1} Sn=n+1(6n3)Sn1(n2)Sn2

应用

  • 【又是走格子】
    在这里插入图片描述
    ( 0 , 0 ) (0,0) (0,0) 走到 ( n , n ) (n,n) (n,n)的格子,每次可以向右上走一格/向右走一格/向上走一格,且不超过对角线的方案数。 [ 4 ]

总结

  • 卡特兰数 最常用到
    默慈金数、那罗延数、施罗德数是卡特兰数的拓展
  • 每者的几何意义都有和走格子有关:
    卡特兰数 C a t ( n ) Cat(n) Cat(n) ( 0 , 0 ) (0,0) (0,0) 走到 ( n , n ) (n,n) (n,n),移动规则为 ( 1 , 0 ) 、 ( 0 , 1 ) (1,0)、(0,1) (1,0)(0,1) 且不穿过对角线的方案数
    施罗德数 S n S_n Sn ( 0 , 0 ) (0,0) (0,0) 走到 ( n , n ) (n,n) (n,n),移动规则为 ( 1 , 0 ) 、 ( 0 , 1 ) 、 ( 1 , 1 ) (1,0)、(0,1)、(1,1) (1,0)(0,1)(1,1) 且不穿过对角线的方案数
    默慈金数 M n M_n Mn ( 0 , 0 ) (0,0) (0,0) 走到 ( n , 0 ) (n,0) (n,0),移动规则为 ( 1 , 0 ) 、 ( 1 , − 1 ) 、 ( 1 , 1 ) (1,0)、(1,-1)、(1,1) (1,0)(1,1)(1,1) 且不穿过 x x x 轴的方案数
    那罗延数 N ( n , k ) N(n,k) N(n,k) ( 0 , 0 ) (0,0) (0,0) 走到 ( 2 n , 0 ) (2n,0) (2n,0),移动规则为 ( 1 , 1 ) 、 ( 1 , − 1 ) (1,1)、(1,-1) (1,1)(1,1) 且不穿过 x x x 轴,有 k k k 个峰的方案数

参考与查阅

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值