题目:洛谷P3200、BZOJ1485、codevs2337。
题目大意:
我们称一个长度为$2n$的数列是有趣的,当且仅当该数列满足以下三个条件:
1. 它是从$1$到$2n$共$2n$个整数的一个排列${a_i}$;
2. 所有的奇数项满足$a_1<a_3<dots<a_{2n-1}$,所有的偶数项满足$a_2<a_4<dots<a_{2n}$;
3. 任意相邻的两项$a{2i-1}$与$a_{2i}(1leqslant ileqslant n)$满足奇数项小于偶数项,即:$a{2i-1}<a{2i}$。
现在的任务是:对于给定的$n$,请求出有多少个不同的长度为$2n$的有趣的数列。因为最后的答案可能很大,所以只要求输出答案$mod P$的值。
解题思路:
找找规律,发现,答案是卡特兰数。
然而$P$并不一定是质数。
所以用另一个公式:$f_n =frac{C^n_{2n}}{n+1}=(2n)!div (n+1)!div n!$。
并且$f_i$保证是一个整数。
由于我们可以很快地求出$n!$内包含某个质数因子的数量(除一下就行了),所以,我们先求出$2n$以内的质数,然后套公式,统计每个质数因子包含的个数,然后快速幂加到答案里即可。
筛质数用线性筛即可。
时间复杂度$O(nlog n)$。
C++ Code:
#include<bits/stdc++.h> #define LoveLive long long int prm[1000005]; bool isnprm[2000005]; int n,md,cnt=0; inline LoveLive pow(LoveLive a,int p){ a%=md; LoveLive ans=1; while(p){ if(p&1)ans=ans*a%md; a=a*a%md; p>>=1; } return ans; } int main(){ scanf("%d%d",&n,&md); int N=n<<1; for(int i=2;i<=N;++i){ if(!isnprm[i])prm[++cnt]=i; for(int j=1;j<=cnt&&prm[j]*i<=N;++j){ isnprm[prm[j]*i]=1; if(i%prm[j]==0)break; } } LoveLive ans=1; for(int i=1,m;i<=cnt;++i){ int s=0; for(m=N;m/=prm[i];)s+=m; for(m=n;m/=prm[i];)s-=m; for(m=n+1;m/=prm[i];)s-=m; ans=ans*pow(prm[i],s)%md; } printf("%lld ",ans); return 0; }