卡特兰数 折线法浅析

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

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

那么考虑 m为任意数字的 方法 显然 咱们须要知道卡特兰数的证实方法  ide

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

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

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

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

此时合法括号序列刚好对应咱们寻找合法路径的方案数 那么全部的方案数对应$\binom{2n}{n}$get

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

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

 

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

咱们此时将从这个点到后面的路径所有关于这个直线翻折过去

那么此时终点变成了 (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 种的方案数  $\binom{2n}{n-1}$

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

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

那么 咱们考虑 若是是刚好m次不合法的方案数 那么 咱们 能够转化成 至少m次不匹配的方案数 - 至少m+1次不匹配的方案数 其实对应到折线图上

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

此时 cat(n,m)-cat(n,m+1)就是答案了 而且 cat(n,m)=$\binom{2n}{n}-\binom{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\n",ans); return 0; }
View Code
相关文章
相关标签/搜索