1485: [HNOI2009]有趣的数列
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1226 Solved: 652
[ Submit][ Status][ Discuss]
Description
我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件:
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)。
对应的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);
}