1. 程式人生 > >bzoj 1485: [HNOI2009]有趣的數列 (卡特蘭數)

bzoj 1485: [HNOI2009]有趣的數列 (卡特蘭數)

1485: [HNOI2009]有趣的數列

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1226  Solved: 652
[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



題解:卡特蘭數

這道題打表找規律就會發現其實答案就是卡特蘭數

f[n]=c(2n,n)/n+1=c(2n,n)-c(2n,n-1)

然後分解質因數再合併,即可處理。

但是這道題,除了打表以外,還有更科學的分析方法。可以將題目中的問題轉換成卡特蘭數的經典問題——入棧出棧問題。

確定了奇數位後,偶數位要麼唯一確定,要麼不存在合法的序列。 然後就可以將奇數位看成入棧,偶數位看成出棧,轉換成卡特蘭數的模型。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 2000003
#define LL long long
using namespace std;
int prime[N],pd[N],num[N],mp[N];
int n,p;
void init()
{
	for (int i=2;i<=2000000;i++){
		if (!pd[i]) prime[++prime[0]]=i,mp[i]=prime[0];
		for (int j=1;j<=prime[0];j++){
			if (prime[j]*i>2000000) break;
			pd[prime[j]*i]=1; 
			if (i%prime[j]==0) break;
		}
	}
}
void calc(int x,int val)
{
	int k=x;
	for (int i=1;prime[i]*prime[i]<=k;i++) 
	if (k%prime[i]==0){
		while (k%prime[i]==0) {
			k/=prime[i]; num[i]+=val;
		}
	}
	if (k>1) num[mp[k]]+=val;
}
LL quickpow(LL num,int x)
{
	LL base=num%p; LL ans=1;
	while (x) {
		if (x&1) ans=ans*base%p;
		x>>=1;
	    base=base*base%p;
	}
    return ans;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d",&n,&p);
	init();
	for (int i=n+1;i<=2*n;i++) calc(i,1);
	for (int i=1;i<=n;i++) calc(i,-1);
	calc(n+1,-1);
	//for (int i=1;i<=n;i++)  cout<<num[i]<<" ";
	//cout<<endl;
	LL ans=1;
	for (int i=1;i<=prime[0];i++)
	 ans=ans*quickpow((LL)prime[i],num[i])%p;
	printf("%I64d\n",ans);
}