zoukankan      html  css  js  c++  java
  • 卡特兰数相关总结

    一、前置知识

    在笛卡尔坐标系中,起点为((0,0)),终点为((n,m)),每一步只能向上或向右走的方案数为(C_{n+m}^n)

    二、基本卡特兰数

    首先亮出卡特兰数的基本公式:

    [Cat_n = frac{C_{2n}^n}{n + 1} ]

    那么卡特兰数有许多经典问题,我们来从最经典的入手。

    例一:在平面直角坐标系中,起点为((0,0)),终点为((n,n)),第一步只能向上走。在之后的移动中可以向上走或向右走,但不能超出直线(y=x) 。求方案数。

    首先这个方案数可以表示为从(0,0)走到(n,n)的总方案数减去至少超出一次的方案数。

    从(0,0)走到(n,n)的总方案数即为(C_{2n}^n).

    现在考虑至少超出一次的方案数。当我们刚刚超出直线(y = x)的时候,我们一定处于直线(y = x - 1)上,把此时所在的点记为点A。现在我们做这样一件事情,把原本从点A到终点的路径围绕直线(y = x - 1)翻折,那么这条路径就变成从原点到点((n+1,n-1))的一条路径。

    我们又可以发现,所有违法的路径与所有从原点到((n + 1, n - 1))的路径构成一一对应关系。证明极其简单,在脑子里想一下就行了。

    所以至少超出一次的方案数就是(C_{(n+1)+(n-1)}^{n+1}=C_{2n}^{n+1})

    最终的答案即为(C_{2n}^n - C_{2n}^{n+1})。化简后得到(frac{C_{2n}^n}{n + 1})

    三、扩展卡特兰数

    例二:其他条件与例一相同,唯一不一样的是把直线(y = x) 改为直线(y = x - m)((mgeq 1))

    有了刚才的思路,我们很容易想到把违法的路径从点A到终点的部分围绕直线(y = x - m - 1)翻折,可以得到从原点到((n - m - 1, n +m + 1))的路径。同样,两者构成一一对应关系。

    最终的答案即为(C_{2n}^{n} - C_{2n}^{n - m - 1})

    四、应用

    看一道真实的考试题。

    如果我们把左括号看成上文例题中的“向上走一步”,右括号看成“向右走一步”,我们会发现本题中所说的“至少有(2 imes m)个括号失配”就是“在走的过程中,接触过直线(y=x-m)的一条路径”。注意:是“接触过直线(y=x-m)”,不能超出,也不能不接触。

    所以方案数就应该为“不超过(y=x- m)”减去“不超过(y=x-m-1)

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    #define LL long long
    #define FILEIN(s) freopen(s".in", "r", stdin)
    #define FILEOUT(s) freopen(s".out", "w", stdout)
    #define mem(s, v) memset(s, v, sizeof(s))
    
    inline LL read(void) {
    	LL x = 0, f = 1; char ch = getchar();
    	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    	return f * x;
    }
    
    const int maxn = 1000005, mod = 998244353;
    
    LL n, m;
    LL fac[maxn << 1], facinv[maxn << 1];
    
    inline LL power(LL a, LL b) {
    	LL ret = 1;
    	for (; b; b >>= 1) {
    		if (b & 1) ret = ret * a % mod;
    		a = a * a % mod;
    	}
    	return ret;
    }
    
    inline void init(void) {
    	fac[0] = 1;
    	for (register int i = 1; i <= 2000000; ++i) fac[i] = fac[i - 1] * i % mod;
    	facinv[2000000] = power(fac[2000000], mod - 2);
    	for (register int i = 1999999; i >= 0; --i) {
    		facinv[i] = facinv[i + 1] * (i + 1) % mod;
    	}
    }
    
    inline LL C(LL n, LL m) {
    	return fac[n] * facinv[m] % mod * facinv[n - m] % mod;
    }
    
    inline LL excatalan(LL n, LL m) {
    	return ((C(2 * n, n) - C(2 * n, n - m - 1)) % mod + mod) % mod;
    }
    
    int main() {
    	n = read(); m = read();
    	init();
    	if (!m) return printf("%lld
    ", C(2 * n, n) * power(n + 1, mod - 2) % mod), 0;
    	return printf("%lld
    ", ((excatalan(n, m) - excatalan(n, m - 1)) % mod + mod) % mod), 0;
    }
    
    
  • 相关阅读:
    CS224n, lec 10, NMT & Seq2Seq Attn
    CS231n笔记 Lecture 11, Detection and Segmentation
    CS231n笔记 Lecture 10, Recurrent Neural Networks
    CS231n笔记 Lecture 9, CNN Architectures
    CS231n笔记 Lecture 8, Deep Learning Software
    CS231n笔记 Lecture 7, Training Neural Networks, Part 2
    pytorch坑点排雷
    Sorry, Ubuntu 17.10 has experienced an internal error
    VSCode配置python插件
    tmux配置与使用
  • 原文地址:https://www.cnblogs.com/little-aztl/p/11763966.html
Copyright © 2011-2022 走看看