BZOJ 1485: [HNOI2009]有趣的数列(卡特兰数)

传送门

解题思路

  因为总共是一个排列,那么确定了奇数项是哪些,偶数项就确定了。怎么判断奇数项是否合法呢,其实由于第三个限制,可以把这个数列看成一个括号序列,奇数项为\((\),偶数项为\()\),那么合法方案数自然是卡特兰数了。。模数不是质数,而且\(n^2\)会超时,就只能用\(ans=\frac{\dbinom{2n}{n}}{n+1}\)这个式子了,化简一下就是\(ans=\dfrac{\prod\limits_{i=n+2}^{2n}i}{\prod\limits_{i=1}^ni}\),然后筛一下质数,把这个式子拆成若干个素数次幂乘积形式,然后快速幂就能做了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int MAXN = 2000005;
typedef long long LL;

int n,MOD,cnt[MAXN],tot,prime[MAXN];
int vis[MAXN],ans=1;

inline int fast_pow(int x,int y){
	int ret=1;
	for(;y;y>>=1){
		if(y&1) ret=(LL)ret*x%MOD;
		x=(LL)x*x%MOD;	
	}
	return ret;
}

int main(){
	scanf("%d%d",&n,&MOD);vis[1]=1;
	for(int i=2;i<=2*n;i++){
		if(!vis[i]) vis[i]=i,prime[++tot]=i;
		for(int j=1;j<=tot && (LL)i*prime[j]<=2*n;j++){
			vis[i*prime[j]]=prime[j];
			if(!(i%prime[j])) break;
		}
	}
	for(int i=2;i<=n;i++) cnt[i]=-1;
	for(int i=n+2;i<=n*2;i++) cnt[i]=1;
	for(int i=n*2;i>1;i--) {
		if(vis[i]==i) ans=((LL)ans*fast_pow(i,cnt[i])%MOD);
		else cnt[vis[i]]+=cnt[i],cnt[i/vis[i]]+=cnt[i];	
	}
	printf("%d\n",ans);
	return 0;	
}
posted @ 2018-12-04 21:28  Monster_Qi  阅读(185)  评论(0编辑  收藏  举报