P3200 [HNOI2009]有趣的数列--洛谷luogu

---恢复内容开始---

题目描述

我们称一个长度为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的值。

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1: 复制
3 10
输出样例#1: 复制
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)。

-------------------------------------------------------------------------------------------------

做之前我知道这是一道
关于Catalan数的问题
但是
当我读完题目后
我是真的蒙了
它是怎么和Catalan数挂上关系的
于是
我再次被卑微了
————————————————————————————————————————————————————————————————————————————————————

考察这样相邻的两项:a_{2i-1}a2i1a_{2i}a2i,根据题目的第二条原则显然有a_{2i-1}<a_{2i}a2i1<a2i

而根据第一条原则又有奇数是递增的。

所以有a_1<a_3<...<a_{2i-1}<a_{2i}a1<a3<...<a2i1<a2i

这个时候可以联想到这道经典的题目

我们可以将奇数项看为入栈,偶数项看为出栈。

发现和入栈次数必须大于出栈次数的条件恰好相符。

所以可以使用卡特兰数求解。

但是直接暴力用公式是会爆的

所以需要一些优化

 

#include <bits/stdc++.h>
using namespace std;
long long i,j,n,m,ans,sum,x,a[2000005],b[2000005],p[2000005];
int main()
{
    scanf("%lld%lld",&n,&m);

    for (i=2; i<=2*n; i++)
    {
        if (a[i]==0) 
        {
            sum++; 
            p[sum]=i; 
            a[i]=sum;
        }//sum累加,p数组标记第sum个质数为i ,a[i]表示i这个数是由第sum个质数筛得的
    for (j=1; j<=sum; j++) //枚举当前已得到的所有质数,用这些质数筛选出在范围内的可以被这些质数筛得合数
        if (p[j]*i<=2*n) 
            a[p[j]*i]=j; 
        else 
            break; 
    //并把筛到的合数标记是被第几个质数 筛到的 
    }

    for (i=n+2; i<=2*n; i++)
    {
        x=i;
        while (x>1) 
        {
            b[a[x]]++; 
            x=x/p[a[x]];
        } //a[x]表示第x个数是由第a[x]个质数筛得的, 然后把表示第a[x]个质数个数的数组累加 
    //做完上述操作后将x除以已累加的质数 
    }

    for (i=2; i<=n; i++)
    {
        x=i;    
        while (x>1) 
        {
            b[a[x]]--; 
            x=x/p[a[x]];
        }   //重复上述操作 
    }
    ans=1;
    for (i=1; i<=sum; i++)
        for (j=1; j<=b[i]; j++) 
            ans=ans*p[i]%m; //p[i]表示第i个质数是什么,b[i]表示有第i个质数共有几个 
    printf("%lld",ans);
    return 0;   
}

 

 

posted @ 2019-02-10 19:41  darrrr  阅读(147)  评论(0编辑  收藏  举报