bzoj1485: [HNOI2009]有趣的数列(Catalan数)

1485: [HNOI2009]有趣的数列

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 2105  Solved: 1117
[Submit][Status][Discuss]

Description

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

    (1)它是从1到2n共2n个整数的一个排列{ai};

    (2)所有的奇数项满足a1<a3<…<a2n-1,所有的偶数项满足a2<a4<…<a2n

    (3)任意相邻的两项a2i-1与a2i(1≤i≤n)满足奇数项小于偶数项,即:a2i-1<a2i

    现在的任务是:对于给定的n,请求出有多少个不同的长度为2n的有趣的数列。因为最后的答案可能很大,所以只要求输出答案 mod P的值。

 

Input

输入文件只包含用空格隔开的两个整数n和P。输入数据保证,50%的数据满足n≤1000,100%的数据满足n≤1000000且P≤1000000000。

 

Output

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

 

Sample Input

3 10

Sample Output

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)。

HINT

 

Source

 

/*
可以把形成序列的过程看成加数的过程,从小到大逐步加(这显然满足限制一),
然后加数的条件一是从小到大依次放奇数位或偶数位,因此也满足限制二,
然后无论何时偶数位上的数一定要大于等于奇数位上的数,这样也满足了限制三,
那么问题就转化成了按照如上条件放数的方案数,联系第二个条件,
也就是无论何时偶数位上的数一定要大于等于奇数位上的数,可以联想到栈,进栈次数一定要大于等于出栈次数,
不妨把放在偶数位上看成进栈,放在奇数位上看成出栈,这样就是求一个出栈的序列,也就是Catalan数列。
由于模数不一定是质数,可以分解质因数求。 
*/
#include<bits/stdc++.h>

#define ll long long
#define N 2000007

using namespace std;
int n,mod,cnt;
ll pri[N],min_pri[N],num[N],ans=1;
bool use[N];

void getpri()
{
    for (int i=2; i<=2*n; i++)
    {
        if (!use[i]) pri[++cnt]=i,min_pri[i]=cnt;
        for (int j=1; pri[j]*i<=2*n&&j<=cnt; j++)
        {
            use[pri[j]*i]=1,min_pri[pri[j]*i]=j;
            if (i%pri[j]==0) break;
        }
    }
}

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

void add(int x,int opt)
{
    while (x!=1)
    {
        num[min_pri[x]]+=opt;
        x/=pri[min_pri[x]];
    }
}

int main()
{
    scanf("%d%d",&n,&mod);
    getpri();
    for (int i=2*n; i>n; i--) add(i,1);
    for (int i=1; i<=n+1; i++) add(i,-1);
    for (int i=1; i<=cnt; i++) ans=ans*ksm(pri[i],num[i])%mod;
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2018-11-06 18:31  安月冷  阅读(205)  评论(0编辑  收藏  举报