卡特兰数 - HNOI 2009 有趣的数列 - 洛谷 P3200

卡特兰数 - HNOI 2009 有趣的数列 - 洛谷 P3200

我 们 称 一 个 长 度 为 2 n 的 数 列 是 有 趣 的 , 当 且 仅 当 该 数 列 满 足 以 下 三 个 条 件 : 我们称一个长度为 2n 的数列是有趣的,当且仅当该数列满足以下三个条件: 2n

  • 它 是 从 1 到 2 n 共 2 n 个 整 数 的 一 个 排 列 a i ; 它是从 1 到 2n 共 2n 个整数的一个排列 {a_i}; 12n2nai

  • 所 有 的 奇 数 项 满 足 a 1 < a 3 < ⋯ < a 2 n − 1 , 所 有 的 偶 数 项 满 足 a 2 < a 4 < ⋯ < a 2 n ; 所有的奇数项满足 a_1a1<a3<<a2n1a2<a4<<a2n

  • 任 意 相 邻 的 两 项 a 2 i − 1 与 a 2 i ( 1 ≤ i ≤ n ) 满 足 奇 数 项 小 于 偶 数 项 , 即 : a 2 i − 1 < a 2 i 。 任意相邻的两项 a_{2i−1} 与 a_{2i }(1≤i≤n) 满足奇数项小于偶数项,即:a_{2i−1}a2i1a2i(1in)a2i1<a2i

任 务 是 : 对 于 给 定 的 n , 请 求 出 有 多 少 个 不 同 的 长 度 为 2 n 的 有 趣 的 数 列 。 任务是:对于给定的 n,请求出有多少个不同的长度为 2n 的有趣的数列。 n2n

因 为 最 后 的 答 案 可 能 很 大 , 所 以 只 要 求 输 出 答 案 m o d   P 的 值 。 因为最后的答案可能很大,所以只要求输出答案 mod\ P 的值。 mod P

输入格式

只包含用空格隔开的两个整数 n 和 P。

输出格式

仅含一个整数,表示不同的长度为 2n 的有趣的数列个数 mod P 的值。

数据范围

1 ≤ n ≤ 1 0 6 , 2 ≤ P ≤ 1 0 9 1≤n≤10^6, 2≤P≤10^9 1n106,2P109

输入样例:

3 10

输出样例:

5

样例解释

对应的 5 个有趣的数列分别为 {1,2,3,4,5,6},{1,2,3,5,4,6},{1,3,2,4,5,6},{1,3,2,5,4,6},{1,4,2,5,3,6}。


分析:

本 题 序 列 数 量 为 2 n , n = 3 时 输 出 5 = C 6 3 − C 6 2 , 暗 示 本 题 与 卡 特 兰 数 相 关 。 本题序列数量为2n,n=3时输出5=C_{6}^{3}-C_{6}^{2},暗示本题与卡特兰数相关。 2nn=35=C63C62

卡 特 兰 数 问 题 的 一 般 形 式 : { ① 、 递 推 式 : f ( n ) = f ( 1 ) ⋅ f ( n − 1 ) + f ( 2 ) ⋅ f ( n − 2 ) + . . . ( 求 二 叉 树 的 个 数 ) ② 、 性 质 : 任 意 前 缀 中 , A 的 数 量 ≥ B 的 数 量 。 卡特兰数问题的一般形式:\begin{cases}①、递推式:f(n)=f(1)·f(n-1)+f(2)·f(n-2)+...(求二叉树的个数)\\\\②、性质:任意前缀中,A的数量≥B的数量。\end{cases} f(n)=f(1)f(n1)+f(2)f(n2)+...()AB

如:组合数学 -卡特兰数 - 满足条件的01序列

题 中 要 求 0 的 个 数 ≥ 1 的 个 数 , 对 应 到 坐 标 轴 上 , 设 x 为 0 的 个 数 , y 为 1 的 个 数 , 需 满 足 x ≥ y 。 题中要求0的个数≥1的个数,对应到坐标轴上,设x为0的个数,y为1的个数,需满足x≥y。 01x0y1xy

本题中:

我 们 需 要 确 定 哪 些 数 是 奇 数 项 , 哪 些 数 是 偶 数 项 , 再 分 别 将 奇 数 项 偶 数 项 排 序 , 就 能 够 确 定 一 种 方 案 。 我们需要确定哪些数是奇数项,哪些数是偶数项,再分别将奇数项偶数项排序,就能够确定一种方案。

现 在 从 : 1 , 2 , 3 , . . . , 2 n 中 , 从 前 到 后 依 次 确 定 每 个 数 属 于 奇 数 项 还 是 偶 数 项 , 现在从:1,2,3,...,2n中,从前到后依次确定每个数属于奇数项还是偶数项, 123...2n

我 们 需 要 保 证 , 任 意 一 种 方 案 的 前 缀 中 , 奇 数 项 的 个 数 不 能 够 小 于 偶 数 项 的 个 数 。 我们需要保证,任意一种方案的前缀中,奇数项的个数不能够小于偶数项的个数。

原因:

假 设 某 个 前 缀 的 奇 数 项 个 数 为 k 1 , 偶 数 项 个 数 为 k 2 , 且 k 1 < k 2 , 假设某个前缀的奇数项个数为k_1,偶数项个数为k_2,且k_1k1k2k1<k2

k 1 < k 2 , 即 前 k 1 + k 2 个 数 中 , 偶 数 项 的 个 数 更 多 , 说 明 偶 数 项 的 最 后 一 项 之 前 , 必 有 奇 数 项 还 未 确 定 , k_1k1<k2k1+k2

因 为 我 们 是 从 小 到 大 选 择 各 个 位 置 上 的 数 的 , 那 么 未 确 定 的 奇 数 项 将 填 入 更 大 的 数 , 因为我们是从小到大选择各个位置上的数的,那么未确定的奇数项将填入更大的数,

这 将 不 满 足 条 件 : 任 意 相 邻 的 两 项 a 2 i − 1 与 a 2 i ( 1 ≤ i ≤ n ) 满 足 奇 数 项 小 于 偶 数 项 。 这将不满足条件:任意相邻的两项 a_{2i−1} 与 a_{2i }(1≤i≤n) 满足奇数项小于偶数项。 a2i1a2i(1in)

举例:

假 设 有 2 项 奇 数 项 已 确 定 : a 1 = 1 , a 3 = 3 。 假设有2项奇数项已确定:a_1=1,a_3=3。 2a1=1a3=3

3 项 偶 数 项 已 确 定 : a 2 = 2 , a 4 = 4 , a 6 = 5 , 3项偶数项已确定:a_2=2,a_4=4,a_6=5, 3a2=2a4=4a6=5

那 么 a 5 的 值 必 大 于 5 , 即 a 5 > a 6 , 不 满 足 条 件 。 那么a_5的值必大于5,即a_5>a_6,不满足条件。 a55a5>a6

因 此 , 本 题 中 的 重 要 性 质 : 因此,本题中的重要性质: 任意前缀的奇数项个数 ≥ 偶数项个数。

我 们 把 每 一 个 奇 数 项 当 作 一 个 0 , 偶 数 项 当 作 一 个 1 , 那 么 问 题 就 转 化 为 《 满 足 条 件 的 01 序 列 》 这 道 题 。 我们把每一个奇数项当作一个0,偶数项当作一个1,那么问题就转化为《满足条件的01序列》这道题。 0101

P 不 确 定 为 质 数 , 所 以 本 题 不 方 便 求 逆 元 , 故 用 卡 特 兰 数 差 的 形 式 来 求 解 : C 2 n n − C 2 n n − 1 。 P不确定为质数,所以本题不方便求逆元,故用卡特兰数差的形式来求解:C_{2n}^n-C_{2n}^{n-1}。 P便C2nnC2nn1

通 过 定 义 , 分 解 阶 乘 质 因 数 + 快 速 幂 来 求 组 合 数 。 通过定义,分解阶乘质因数+快速幂来求组合数。 +

代码:

#include

#define ll long long

using namespace std;

const int N=2e6+10;

int n,mod;
int primes[N],cnt;
bool st[N];

void get_prime(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i]) primes[cnt++]=i;
        for(int j=0;primes[j]*i<=n;j++)
        {
            st[primes[j]*i]=true;
            if(i%primes[j]==0) break;
        }
    }
}

int get(int n,int p)
{
    int s=0;
    while(n) s+=n/p, n/=p;
    return s;
}

int quick_pow(int a,int b,int mod)
{
    int res=1;
    while(b)
    {
        if(b&1) res=(ll)res*a%mod;
        a=(ll)a*a%mod;
        b>>=1;
    }
    return res;
}

int C(int n,int m)
{
    int res=1;
    for(int i=0;i<cnt;i++)
    {
        int p=primes[i];
        int s=get(n,p)-get(m,p)-get(n-m,p);
        res=((ll)res*quick_pow(p,s,mod))%mod;
    }
    return res;
}

int main()
{
    cin>>n>>mod;
    
    get_prime(2*n);
    
    cout<<(C(2*n,n)-C(2*n,n-1)+mod)%mod<<endl;
    
    return 0;
}

你可能感兴趣的:(数学)