bzoj 1485 : [HNOI2009]有趣的数列

  

1485: [HNOI2009]有趣的数列

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 1284  Solved: 688
[Submit][Status][Discuss]

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。输入数据保证,50%的数据满足n≤1000,100%的数据满足n≤1000000且P≤1000000000。

Output

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

Sample Input

3 10

Sample Output

5

 
这道题正着想很不好想,所以我们可以倒过来想。
把[1,2n]的排列当成2n个位置,原数列中每个位置当成一个数x,那么考虑从1到2n依次填现在的每个位置。

然后就很好做了,对于每个奇数的x,填完后一定是有序的,偶数同理,所以可以把奇数视为0,偶数视为1,得到一个合法的01序列后一定可以按顺序还原出数列。

那么当前填了a个0,a+1个1,那么说明原数列中有个位置2i选了,而2i-1还没选,则a[2i-1]>a[2i],显然是违法的。

因为每个时刻0>1,通项为卡特兰数列。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define ll long long
 6 #define N 2000005
 7 using namespace std;
 8 int n,p;
 9 int su[N],tot,pr[N],fan[N];
10 const int inf = 2000000;
11 void shai()
12 {
13     for(int i=2;i<=inf;i++)
14     {
15         if(!pr[i])
16         {
17             su[++tot]=i;
18             fan[i]=tot;
19             pr[i]=i;
20         }
21         for(int j=1;j<=tot&&su[j]<=pr[i]&&su[j]*i<=inf;j++)
22         {
23             pr[su[j]*i]=su[j];
24         }
25     }
26     return ;
27 }
28 int num[N];
29 inline void fft(int x,int z)
30 {
31     while(x!=1)
32     {
33         num[fan[pr[x]]]+=z;
34         x/=pr[x];
35     }
36     return ;
37 }
38 int main()
39 {
40     scanf("%d%d",&n,&p);
41     shai();
42     for(int i=2;i<=n*2;i++)fft(i,1);
43     for(int i=2;i<=n;i++)fft(i,-2);
44     fft(n+1,-1);
45     ll ans=1;
46     for(int i=1;i<=tot;i++)
47     {
48         for(int j=1;j<=num[i];j++)
49         {
50             ans=ans*su[i]%p;
51         }
52     }
53     printf("%lld
",ans);
54     return 0;
55 }

  

  

原文地址:https://www.cnblogs.com/ezyzy/p/6586130.html