卡特蘭數 折線法淺析

這裏以合法括號匹配爲例研究 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
相關文章
相關標籤/搜索