• bzoj1485--HNOI2009有趣的数列--Catalan数


    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

    对应的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)。
     
    题解:
      打表可以看出来,这题的结果是求卡特兰数第n项……
      当然也可以有实际意义,其中奇数项为入栈,偶数项为出栈,用1~2N这些数放进去,就成了卡特兰数。
      卡特兰数的公式 
     
     
      将式子展开以后得到

      因为数据范围很大,且p不保证是质数。我们考虑用分解质因数的方法来表示h(n),因为n+2~2n是在分子上,所以指数为正,而1~n在分母上,所以指数为负。

      对于数字i,当我们要把它的k次方放进结果中时,如果a是i的质因数,就把a的指数和i/a的指数都加k。

      因为上述过程可以迭代进行,故我们只需要求出数字的最小质因数即可,用线性筛求出。

      最后用一下快速幂将每个数字与其指数的结果乘到ans中。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cmath>
     4 #include<cstdio>
     5 #include<cstring>
     6 #define ll long long
     7 using namespace std;
     8 
     9 const int maxn=2000009;//mindiv是最小质因数
    10 int n,p,mindiv[maxn],pri[maxn],cnt[maxn],tot=0;
    11 //cnt数组记录指数的值
    12 ll poww(ll x,ll y,ll m)
    13 {//快速幂
    14     ll ret=1;
    15     while(y)
    16     {
    17         if(y&1)
    18             ret=1ll*ret*x%m;
    19         x=1ll*x*x%m;
    20         y>>=1;
    21     }
    22     return ret;
    23 }
    24 
    25 void getpri(int n)
    26 {//线性筛
    27     for(int i=2;i<=n;i++)
    28     {
    29         if(!mindiv[i])
    30             pri[++tot]=mindiv[i]=i;
    31         for(int j=1,k;j<=tot&&pri[j]<=mindiv[i]&&(k=pri[j]*i)<=n;j++)
    32         {
    33             mindiv[k]=pri[j];
    34             if(i%pri[j]==0)break;
    35         }
    36     }
    37 }
    38 
    39 void calc(int x,int y)
    40 {//计算指数
    41     while(x>1)
    42     {
    43         cnt[mindiv[x]]+=y;
    44         x/=mindiv[x];
    45     }
    46 }
    47 int main()
    48 {
    49     //freopen("eccentric.in","r",stdin);
    50     //freopen("eccentric.out","w",stdout);
    51     scanf("%d%d",&n,&p);
    52     getpri(2*n);
    53     for(int i=1;i<=n;i++)calc(i,-1);
    54     for(int i=n+2;i<=2*n;i++)calc(i,1);
    55     int ans=1;
    56     for(int i=2;i<=n*2;i++)
    57     {
    58         ans=(ll)ans*poww(i,cnt[i],p)%p;
    59     }
    60     printf("%lld
    ",ans);
    61     //fclose(stdin);
    62     //fclose(stdout);
    63     return 0;
    64 }
    View Code
  • 相关阅读:
    常用模块介绍
    正则表达式/re模块
    模块简介/模块的导入/模块的查找顺序/绝对导入和相对导入/软件开发目录规范
    迭代器/for循环本质/生成器/常用内置方法
    函数递归/二分法/列表,字典生成式/三元表达式/匿名函数/内置函数
    闭包函数/装饰器
    函数对象/函数的嵌套定义与调用/名称空间和作用域
    初识函数
    文件处理/光标移动/实时检测
    7-5字符编码和文件处理
  • 原文地址:https://www.cnblogs.com/Beckinsale/p/7610408.html
Copyright © 2020-2023  润新知