P3200 [HNOI2009]有趣的数列--洛谷luogu
---恢复内容开始---
题目描述
我们称一个长度为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 对应的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)。
-------------------------------------------------------------------------------------------------
做之前我知道这是一道
关于Catalan数的问题
但是
当我读完题目后
我是真的蒙了
它是怎么和Catalan数挂上关系的
于是
我再次被卑微了
————————————————————————————————————————————————————————————————————————————————————
考察这样相邻的两项:a_{2i-1}a2i−1与a_{2i}a2i,根据题目的第二条原则显然有a_{2i-1}<a_{2i}a2i−1<a2i。
而根据第一条原则又有奇数是递增的。
所以有a_1<a_3<...<a_{2i-1}<a_{2i}a1<a3<...<a2i−1<a2i。
这个时候可以联想到这道经典的题目。
我们可以将奇数项看为入栈,偶数项看为出栈。
发现和入栈次数必须大于出栈次数的条件恰好相符。
所以可以使用卡特兰数求解。
但是直接暴力用公式是会爆的
所以需要一些优化
#include <bits/stdc++.h> using namespace std; long long i,j,n,m,ans,sum,x,a[2000005],b[2000005],p[2000005]; int main() { scanf("%lld%lld",&n,&m); for (i=2; i<=2*n; i++) { if (a[i]==0) { sum++; p[sum]=i; a[i]=sum; }//sum累加,p数组标记第sum个质数为i ,a[i]表示i这个数是由第sum个质数筛得的 for (j=1; j<=sum; j++) //枚举当前已得到的所有质数,用这些质数筛选出在范围内的可以被这些质数筛得合数 if (p[j]*i<=2*n) a[p[j]*i]=j; else break; //并把筛到的合数标记是被第几个质数 筛到的 } for (i=n+2; i<=2*n; i++) { x=i; while (x>1) { b[a[x]]++; x=x/p[a[x]]; } //a[x]表示第x个数是由第a[x]个质数筛得的, 然后把表示第a[x]个质数个数的数组累加 //做完上述操作后将x除以已累加的质数 } for (i=2; i<=n; i++) { x=i; while (x>1) { b[a[x]]--; x=x/p[a[x]]; } //重复上述操作 } ans=1; for (i=1; i<=sum; i++) for (j=1; j<=b[i]; j++) ans=ans*p[i]%m; //p[i]表示第i个质数是什么,b[i]表示有第i个质数共有几个 printf("%lld",ans); return 0; }