[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:
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; }
我爱学习,学习使我快乐