zoukankan      html  css  js  c++  java
  • 卡特兰数 折线法浅析

    这里以合法括号匹配为例研究 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
  • 相关阅读:
    【小米OJ-找多少个等差数列】动态规划
    【小米OJ-找出可能的合的组合】深搜(dfs)
    【小米OJ-移除k位得到最小值】栈的应用
    【小米OJ-小米兔的轨迹】顺时针蛇形矩形
    Qt常用类
    file_operations
    Proc文件系统
    memset
    Source Insight快捷键大全
    内核内存分配
  • 原文地址:https://www.cnblogs.com/Tyouchie/p/11711540.html
Copyright © 2011-2022 走看看