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;
    }
    
    
  • 相关阅读:
    安装lnmp 时如何修改数据库数据存储地址及默认访问地址
    ubuntu 设置root用户密码并实现root用户登录
    解决ubuntu 远程连接问题
    linux 搭建FTP服务器
    PHP 根据ip获取对应的实际地址
    如何发布自己的composer包
    使用composer安装composer包报Your requirements could not be resolved to an installable set of packages
    laravel 框架配置404等异常页面
    使用Xshell登录linux服务器报WARNING! The remote SSH server rejected X11 forwarding request
    IoTSharp 已支持国产松果时序数据库PinusDB
  • 原文地址:https://www.cnblogs.com/little-aztl/p/11763966.html
Copyright © 2011-2022 走看看