[HNOI2009] 有趣的数列

题目描述

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


虽然好多人是打表找规律发现答案是卡特兰数的2333,但其实你如果学了杨表就会发现这就是一个2*n的杨表,直接列式子就是 (2n)!/(n+1)!/n!。
但是模数不是质数怎么办啊QWQ
一开始想要把P质因数分解然后用二元组表示阶乘最后CRT合并的,而且竟然质因数分解也想写最近新学的Pillard's Rho,这么一算代码至少要5k啊。。。写死算了QWQ
但是想一想,模数不是质数最多也就是做除法比较麻烦。。。那我们干脆就不做除法了,直接把每个质因子在答案里的次数算出来然后都乘起来不就行了吗QWQ
因为只需要算三个阶乘,所以上述方法还是贼快并且贼好写的233333

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2000005;

inline int ksm(int x,int y,const int ha){
	int an=1;
	for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
	return an;
}

int n,p,zs[maxn/10],t=0,ans=1,N;
bool v[maxn];

inline int solve(int x){
	int now=0;
	ll y=x;
	
	while(y<=N) now+=N/y-n/y-(n+1)/y,y*=(ll)x; 
	
	return ksm(x,now,p);
}

int main(){
	scanf("%d%d",&n,&p),N=n<<1;
	
	const int ha=p;
	
	for(int i=2;i<=N;i++){
		if(!v[i]) zs[++t]=i,ans=ans*(ll)solve(i)%ha;
		for(int j=1,u;j<=t&&(u=zs[j]*i)<=N;j++){
			v[u]=1;
			if(!(i%zs[j])) break;
		}
	}
	
	printf("%d\n",ans);
	return 0;
}

  

 
posted @ 2018-06-22 17:06  蒟蒻JHY  阅读(307)  评论(0编辑  收藏  举报