[HNOI2009]有趣的数列 卡特兰数

题面:[HNOI2009]有趣的数列

题解:

  观察到题目其实就是要求从长为2n的序列中选n个放在集合a,剩下的放在集合b,使得集合a和集合b中可以一一对应的使a中的元素小于b。

  2种想法(实质上是一样的)。

  1,相当于前1位中至少要选1个放入a,前3位中至少要选2位放入a,前5位中至少要选3位放入a......前2n - 1位中恰好选n位放入a。

  2,用0表示放入a集合,1表示放入b集合。则每个1都必须有一个左边的0与之匹配,相当于对于任意位置前面0的个数大于等于1的个数。

  不管是哪种,其实都可以看做括号匹配问题。。。。。。所以就是卡特兰数了

  因为n比较大,且p不一定为质数,因此可以用一个数组存下每个质因子有几个,对于乘法就累加,除法就减掉,然后把最后剩下的累乘起来就是答案。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 2001000
 5 #define maxn 2000000
 6 #define LL long long
 7 
 8 int tot, n, p;
 9 int pri[AC], last[AC];
10 LL have[AC], ans;
11 bool ispri[AC];
12 
13 void get()
14 {
15     last[1] = 1;
16     for(R i = 2; i <= maxn; i ++)
17     {
18         if(!ispri[i]) pri[++ tot] = i, last[i] = i;
19         for(R j = 1; j <= tot; j ++)
20         {
21             int now = pri[j];
22             if(i * now > maxn) break;
23             ispri[i * now] = true, last[i * now] = now;
24             if(!(i % now)) break;
25         }
26     }
27 }
28 
29 void pre(){
30     scanf("%d%d", &n, &p);
31 }
32 
33 inline void cal(int x){//分解质因数
34     while(last[x] != 1) ++ have[last[x]], x /= last[x];
35 }
36 
37 inline void cut(int x){
38     while(last[x] != 1) -- have[last[x]], x /= last[x];
39 }
40 
41 inline LL qpow(LL x, int have)
42 {
43     LL ans = 1;
44     while(have)
45     {
46         if(have & 1) ans = ans * x % p;
47         x = x * x % p, have >>= 1;
48     }
49     return ans;
50 }
51 
52 void work()
53 {
54     int b = 2 * n;
55     for(R i = 1; i <= b; i ++) cal(i);
56     for(R i = 1; i <= n; i ++) cut(i), cut(i);
57     cut(n + 1), ans = 1;
58     for(R i = 1; i <= b; i ++)
59         if(have[i]) ans = ans * qpow(i, have[i]) % p;
60     printf("%lld
", ans);
61 }
62 
63 int main()
64 {
65 //    freopen("in.in", "r", stdin);
66     get();
67     pre();
68     work();
69 //    fclose(stdin);
70     return 0;
71 }
View Code
原文地址:https://www.cnblogs.com/ww3113306/p/9919969.html