JZOJ 1353. 有趣的数列

题目

Description

  我们称一个长度为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的值。
 

Input

  从文件中读入数据,输入文件只包含用空格隔开的两个整数n和P。

Output

  输出文件中仅含一个整数,表示不同的长度为2n的有趣的数列个数mod P的值。
 

Sample Input

3 10

Sample Output

5
 

Data Constraint

 
 

Hint

【样例说明】
  对应的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)。
【数据说明】
  50%的数据满足n≤1000且P<=1,000,000
  100%的数据满足n≤1,000,000且P≤1,000,000,000。

 

分析

 

  • 通过暴力打标可得
  • 是卡特兰数
  • C(2n,n)/(n+1)
  • 然后我们直接求
  • 需要用到质因数分解
  • 然后约分得到答案

 

代码

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 #define ll long long
 7 using namespace std;
 8 const int maxn=1e6+5;
 9 int prime[maxn],vis[2*maxn];
10 int cnt=1,id[2*maxn];
11     int n,p;
12 int pri()
13 {
14     for (int i=2;i<=2*n;i++)
15     {
16         if (!vis[i])
17         {
18             prime[cnt]=i;
19             id[i]=cnt++;
20         }
21         for(int j=1;prime[j]*i<=2*n&&j<=cnt;j++)
22         {
23             vis[prime[j]*i]=1;id[prime[j]*i]=j;
24             if(i%prime[j]==0)break;
25         }
26     }
27 }
28 int tot[maxn];
29 void add(int x,int k)
30 {
31     while (x!=1)
32     {
33         tot[id[x]]+=k;
34         x/=prime[id[x]];
35     } 
36 }
37 int main()
38 {
39     ll ans=1;
40     cin>>n>>p;
41     pri();
42     for (int i=n+1;i<=2*n;i++) add(i,1);
43     for (int i=1;i<=n;i++) add(i,-1);
44     add(n+1,-1);
45     for (int i=1;i<=cnt;i++)
46       while (tot[i]) ans=(ans*prime[i])%p,tot[i]--;
47     cout<<ans;
48     return 0;
49 }

 

posted @ 2019-11-06 15:36  Melted_czj  阅读(199)  评论(0编辑  收藏  举报
body { background-color:whitesmoke; } // 修改背景颜色为半透明 #home,#sideBarMain>div,#blog-sidecolumn>div>div,.catListView{ background-color:rgba(255,255,255,0); } // 修改其他边框的颜色