卡特兰数 折线法浅析

这里以合法括号匹配为例研究 Catalan数 

这篇是在模拟赛中总结的关于 研究 恰好 m 个失配的方案数 m=0 的时候 就是我们平时了解到的卡特兰数 合法括号匹配数

那么考虑 m为任意数字的 方法 显然 我们需要知道卡特兰数的证明方法  

首先证明方法有折线法 由折线法 我们不妨引出 另外一种证明思想 我思故我在 嘛

卡特兰数对应的 问题模型都是 在第K次执行 操作2的时候 操作1都是至少执行了K次 那么我们用x轴表示 当前的操作次数 一共需要2n次

然后把操作1当做 向上45走根号2步 操作2当成 向下45走根号2步 那么此时我们就能发现一条到达 (2n,0) 的折线 

那么合法的方案数我们也能发现也就是 这个折线没有任意时刻 跨过x轴 那么我们考虑这个时候 用所有的方案数 减去 不合法的方案数

此时合法括号序列恰好对应我们寻找合法路径的方案数 那么所有的方案数对应$inom{2n}{n}$

那么此时来考虑 怎么求所有不合法的方案数 我们记得 我们在求从(0,0)出发 向上 或者 向右 到达(n,n)

不跨过y=x 的方案数 我们是怎么求证的呢 我们画个图来探讨一下 我真不想画 截yxc神仙的b站上的 组合数学讲解 推荐他的文章 很详细 不太清楚组合数的同学 可以看一下他的视频

现在任意存在一个 不合法 的路径 那么我们找到第一次 跨过 这个直线的部分 到达另外一条直线 也就是图上 绿色 部分

我们此时将从这个点到后面的路径全部关于这个直线翻折过去

那么此时终点变成了 (n-1 n+1)那么所有不合法的路径就对应 所有从(0,0) 到达(n-1,n+1) 所有路径条数 那么方案数我们就显然知道了

此时我们回到上面的折线法 我们考虑 每次45度 这种方法 也是把第一次 跨过x轴 到达y = -1 这个直线的时候 K 之后 K+1~2n 之间的路径 全部关于 y = -1 对称过来 

不过是把 y=x 这个基准线 变成x轴 然后把绿色的线 变成y = -1 感觉这种思想很巧妙吧 把难以统计的答案变成容易统计的方案数

那么 终点变成了 (2n,-2) 此时操作1 比操作 少2 那么操作1 有n-1 操作2 有n+1 那么考虑 此时 就是2n 中选择 n-1 种的方案数  $inom{2n}{n-1}$

那么不合法的方案数 也就是 至少一次不匹配的方案数 此时卡特兰数的 我们就证明了出来 有必要把这种翻折 寻找基准线的方法 理解一下。

此时$inom{2n}{n}-inom{2n}{n-1}$ 就是方案数  其实从这个时候 我们不妨思考一下 我们减去的就是对应的 至少一种不匹配的方案数 

那么 我们考虑 如果是恰好m次不合法的方案数 那么 我们 可以转化成 至少m次不匹配的方案数 - 至少m+1次不匹配的方案数 其实对应到折线图上

我们此时把基准线变成y=-m 这个时候 我们求出来 按照 上述的方案数 此时 我们求出来 就是 至少m次 不合法的方案数

此时 cat(n,m)-cat(n,m+1)就是答案了 并且 cat(n,m)=$inom{2n}{n}-inom{2n}{n-m-1} $ 带入即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &x) {
    x=0;T f=1,ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x*=f;
} 
const ll mod=998244353;
ll fac[2002002],inv[2002002],n,m,ans;
inline ll pow(ll a,ll b) {
    ll res=1;
    while(b) {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
inline ll C(ll n,ll m) {
    if(m>n) return 0;
    if(m<0) return 0;
    if(n<0) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
inline ll c(ll n) {return (C(2ll*n,n)-C(2ll*n,n+1)+mod)%mod;}
ll exctl(int n,int m) {return (C(2ll*n,n)-C(2*n,n-m-1)+mod)%mod;}
int main() {
    freopen("excatalan.in","r",stdin);
    freopen("excatalan.out","w",stdout);
    read(n); read(m); 
    fac[0]=1;
    for(int i=1;i<=2*n;i++) {
        fac[i]=fac[i-1]*i;
        fac[i]%=mod;
    }
    inv[2*n]=pow(fac[2*n],mod-2);
    for(int i=2*n-1;i>=0;i--) {
        inv[i]=inv[i+1]*(i+1);
        inv[i]%=mod;
    }
    if(m==0) ans=c(n);
    else ans=(exctl(n,m)-exctl(n,m-1)+mod)%mod;
    printf("%lld
",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Tyouchie/p/11711540.html