zoukankan      html  css  js  c++  java
  • AcWing 889. 满足条件的01序列

    题目传送门

    一、题目描述

    给定 (n)(0)(n)(1),它们将按照某种顺序排成长度为 (2n) 的序列,求它们能排列成的所有序列中,能够满足任意前缀序列中 (0) 的个数都不少于 (1) 的个数的序列有多少个。答案对 (10^9+7) 取模。

    数据范围:(1≤n≤10^5)

    组合计数,卡特兰数

    二、解题思路

    (01) 序列置于坐标系中,起点定于原点。若 (0) 表示向右走,(1) 表示向上走,那么任何前缀中 (0) 的个数不少于 (1) 的个数就转化为,路径上的任意一点,横坐标大于等于纵坐标。题目所求即为这样的合法路径数量。

    下图中,表示从 ((0,0)) 走到 ((n,n)) 的路径,在绿线及以下表示合法,若触碰红线即不合法。

    由图可知,任何一条不合法的路径(如黑色路径),都对应一条从 ((0,0)) 走到 ((n−1,n+1)) 的一条路径(如灰色路径)。而任何一条 ((0,0)) 走到 ((n−1,n+1)) 的路径,也对应了一条从 ((0,0)) 走到 ((n,n)) 的不合法路径。

    答案如图,即卡特兰数。

    三、举个栗子

    假设(n=6):

    1、方案总数

    ((0,0))((6,6))共有多少种走法

    因为从((0,0))((6,6)) 共需要向上共走(6)步,向右共走(6)步,一共(12)步。

    每步两种选择,向上或向右,共(2)种选择。

    如果在其中某(6)个步骤中选择向上走,那么剩下的(6)个步骤就是向右走。

    向上走的方案数就是(LARGE C_{12}^6),向上的方案数定了,其它的步骤只能是向右的,最终的方案数是(LARGE C_{12}^6)

    2、红线的用途

    红线是根据题目要求的画出来的,也是卡特兰数的精华。在红线以下的路径是合法的,红线就是高压线,不能超过红线!因为题目要求任何时刻,(0)要比(1)多,也就是向右的个数要比向上的多!绿色线是相等,一旦达到或超过红线,都是不可以的!

    比如上面图中的黑色线,就是一条不符合要求的线,因为有部分超过了红线。 如果我们计算出来有多少条这样的黑线,从总数中减出去,就是合法的方案数。

    3、黄线的数量计算

    每一条黑线,一旦越过红线的部分,都可以根据红线做轴对称,画出对称的灰线。这样,所有到((6,6))的黑线,都可以找到一条到((5,7))的灰线,换句话说,我们只要计算出从((0,0))((5,7))的方案数,也就是到((0,0))((6,6))的越红线(也就是黑线)的方案数个数。从((0,0))((5,7))的方案数:(LARGE C_{12}^5)

    4、合法方案数

    (C_{12}^{6} - C_{12}^{5})

    这是以((6,6))为例子,泛化后就是 $ LARGE C_{2n}^{n} - C_{2n}^{n-1}= frac{C_{2n}^{n}}{n+1}$
    $ large = frac{2n imes (2n-1) imes ... imes (n+1) }{n imes (n-1) imes (n-2) imes ... imes 1} mod (m) imes inv(n+1)$

    $ = 2n imes (2n-1) imes ... imes (n+1) imes inv(n+1) imes inv(n) imes inv(n-1) imes inv(n-2) imes ... mod(m)$

    四、C++ 代码

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long LL;
    const int mod = 1e9 + 7;
    int n;
    
    int qmi(int a, int b) {
        int res = 1;
        while (b) {
            if (b & 1) res = (LL) res * a % mod;
            b >>= 1;
            a = (LL) a * a % mod;
        }
        return res;
    }
    
    int main() {
        //优化输入
        ios::sync_with_stdio(false);
        cin >> n;
        int res = 1;
    
        //计算C(2n,n) mod 1e9+7
        for (int i = 2 * n; i > n; i--) res = (LL) res * i % mod;
        for (int i = n + 1; i; i--) res = (LL) res * qmi(i, mod - 2) % mod;
    
        cout << res << endl;
        return 0;
    }
    

    五、记忆卡特兰数特征

    数列的前几项为:(1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862)

  • 相关阅读:
    自学入门 Python 优质中文资源索引
    Crawlab Lite 正式发布,更轻量的爬虫管理平台
    一款被大厂选用的 Hexo 博客主题
    源码解读 Golang 的 sync.Map 实现原理
    探究 Go 语言 defer 语句的三种机制
    一道快速考察 Python 基础的面试题
    编写自己的 GitHub Action,体验自动化部署
    Python 2 与 3 共存了 11 年,新年就要和它道别
    30 年前的圣诞节,Python 序章被谱写
    文言文编程火了,可我完全学不懂
  • 原文地址:https://www.cnblogs.com/littlehb/p/15388636.html
Copyright © 2011-2022 走看看