1485: [HNOI2009]有趣的数列
Time Limit: 10 Sec Memory Limit: 64 MB
Submit: 1348 Solved: 729
[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


【分析】

把问题转换为一个2*n的矩阵,从左到右一个一个填数字,要求左边<右边,下边<上边。把在上面填视为-1,下面填视为+1。

机智的发现这就是个Catalan数。
令h(n)为Catalan数第n项

h(n)=C(2n,n)n+1  

暴力质因数分解计算


【代码】

//bzoj 1485
//H(n)=C(2n,n)/(n+1)
#include<cstdio>
#define N 1000000
#define fo(i,j,k) for(i=j;i<=k;i++)
const int mxn=2000005;
int n,m;
long long ans=1;
int pri[mxn],vis[mxn],g[mxn],bo[mxn];
inline void init()
{
int i,j;
fo(i,2,2*N)
{
if(!vis[i]) pri[++pri[0]]=i,g[i]=i;
for(j=1;j<=N && (long long)i*pri[j]<=2*N;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0)
{
g[i*pri[j]]=g[i];
break;
}
g[i*pri[j]]=pri[j];
}
}
}
inline void solve(int x,int f)
{
while(x>1)
bo[g[x]]+=f,x/=g[x];
}
int main()
{
init();
int i,j,p;
scanf("%d%d",&n,&p);
fo(i,n+2,n+n) solve(i,1);
fo(i,1,n) solve(i,-1);
fo(i,1,n+n)
while(bo[i])
bo[i]--,ans=ans*i%p;
printf("%lld\n",ans);
return 0;
}