BZOJ1485 [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的数组。奇数叫做A,偶数叫做B。问题转化为把2n个数从小到大往数组里填,每次可以选填入A或者填入B(填在当前A/B数组的末尾)。还要保证A[i]<B[i]。

要保证A[i]<B[i]等同于任何时刻数组A的长度都要>=B的长度。

发现这就是经典的卡特兰数模型。可以理解为出入栈顺序或者在二维平面上走且不能超过y=x这条线

答案就是(2nn)n+1\frac{\binom{2n}n}{n+1}

由于模数非质数,就要预处理1~2n质数然后根据组合定义式统计。

预处理最小质数后可以做到O(n)O(n)

CODE

最后的快速幂是O(×log)=O(n)O(质数个数\times log)=O(n)。因为质数个数是O(nlogn)O(\frac n{logn})级别的。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 2000005;

int n, mod, cur, mp[MAXN], p[MAXN/10], cnt[MAXN];

inline int qpow(int a, int b) {
	int re = 1;
	while(b) {
		if(b&1) re = 1ll * re * a % mod;
		a = 1ll * a * a % mod; b >>= 1;
	}
	return re;
}

int main ()
{
	scanf("%d%d", &n, &mod);
	for(int i = 2; i <= 2*n; ++i) {
		if(!mp[i]) mp[i] = i, p[++cur] = i;
		for(int j = 1; p[j] * i <= 2*n; ++j) {
			mp[i*p[j]] = p[j];
			if(i % p[j] == 0) break;
		}
	}
	for(int i = 2; i <= n; ++i) --cnt[i];
	for(int i = n+2; i <= 2*n; ++i) ++cnt[i];
	for(int i = 2*n; i > 1; --i)
		if(mp[i] < i) {
			cnt[mp[i]] += cnt[i];
			cnt[i/mp[i]] += cnt[i];
		}
	int ans = 1;
	for(int i = 2; i <= 2*n; ++i)
		if(mp[i] == i)
			ans = 1ll * ans * qpow(i, cnt[i]) % mod;
	printf("%d\n", ans);
}
posted @ 2019-12-14 14:50  _Ark  阅读(110)  评论(0编辑  收藏  举报