首先next_permutation打表,发现Cat规律。

其实考试的时候这么做没什么问题,而且可以节省异常多的时间,那么现在我们来想一下why。

首先我拿模型法解释一下,我们把2n个数看成2n个人,既然分成奇数和偶数两种比较方式,那么我让他们站成两排,每一排有n个人,这n个人的身高递增,且,第二排的人必须高于第一排,那么这个问题就变成了:

有2n个身高互不相同人站成两排,每排n人,要求右边的人比左边的人高,后面的人比前面的人高,问我有几种排队方案。

这是一个Cat的模型,既然先站哪一排无所谓,我就让某个位置必须先站上第一排的人再站上第二排的人,如果我将站在第一排看做是0,站在第二排看做是1,那么既然每个1前面一定有一个比他矮的人,则一定有一个0,那么就又转化成了求0,1序列,这是一个更加经典的Cat模型。(如果这里理解不了可以上网搜搜)

然后再拿折线法解释一下,我们把偶数项看做x轴上的数,因为他们是单增的,把奇数项看做y轴上的数,由于奇数项小于与之对应偶数项,也就是不能越过y=x,函数的变化就好像只能向右走和向上走。这个问题在上一篇博客中有详细的解法。

所以我们明白它是让我们求Cat,可是P不一定是质数,逆元的问题很恶心。

所以我们采用唯一分解来做。首先线性筛筛出2n以内的所有素数,然后我们枚举每个素数,对n执行以下操作:将n不断的除以这个素数,并将商加入s变量,最终s的值就是n!在算术基本定理拆分后,这个素数的指数。举个例子:

8!=27*32*5*7,8/2=4,4/2=2,2/2=1,1/2=0。4+2+1+0=7。

20!=218……,20/2=10,10/2=5,5/2=2,2/2=1,1/2=0。10+5+2+1+0=18。

大家可以自己随便试两个。

这是为什么呢?(下述i为质数)首先1~n中含有i这个因子的数有n/i个(1),含有i2这个因子的数有n/i2个(2),……含有im这个因子的数有n/im个(m)。那么我们分层计算贡献,首先(1)中有n/i个i,加上,(2)中有2*n/i2个i,但不要忘了,我们在(1)算过每个数中的一个i,那么它们的贡献只有n/i2个i,同理,向后类推,最后n!中i的个数为∑n/pi,与上述模拟过程一致。

那么我们来证明一下复杂度,首先根据小于N的质数约有N/lnN个,我们第一层枚举的代价就是O(N/lnN),然后观察上述过程,我们的问题规模不断缩小,如上述二例,都是1/2、1/2的速度在缩小,对于其他素数类似,我们取最坏O(log2N),那么总复杂度

O(N/lnN*log2N),这玩意换换底就是O(N/ln2),1/ln2≈1.44,撇掉,大约O(N),(这是我自己证的,网上目测没有,如果有异议请指出,应该没什么问题吧……)

然后分子加分母减拆完了拿快速幂一乘就完事了。(快速幂并不影响上述复杂度,因为qpow也是O(logk)的,就当常数大了吧)。

(底下代码有表机,勾掉的调试略多,可以用来自己见证一下上面那个算法的正确性)

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<cstdio>
  6. #include<vector>
  7. #include<queue>
  8. #include<stack>
  9. #include<set>
  10. #include<map>
  11. using namespace std;
  12. int n,P,/*a[20],*/ans=;
  13. /*bool check(){
  14. for(int i=1;i<=n;i++)
  15. if(a[i*2-1]>a[i*2]) return 0;
  16. for(int i=3;i<=n*2;i++)
  17. if(a[i]<a[i-2]) return 0;
  18. return 1;
  19. }*/
  20. int prime[],prime_num;
  21. bool v[];
  22. void doprime(){
  23. for(int i=;i<=*n+;i++){
  24. if(!v[i]) prime[++prime_num]=i;
  25. for(int j=;j<=prime_num&&i*prime[j]<=*n+;j++){
  26. v[prime[j]*i]=;
  27. if(i%prime[j]==) break;
  28. }
  29. }
  30. }
  31. int qpow(int x,int k){
  32. int val=;
  33. for(;k;k>>=,x=1ll*x*x%P)
  34. if(k&) val=1ll*val*x%P;
  35. return val%P;
  36. }
  37. int main(){
  38. //打表找规律系列。。。
  39. /* while(1){
  40. ans=0;
  41. scanf("%d%d",&n,&P);
  42. for(int i=1;i<=(n<<1);i++)
  43. a[i]=i;
  44. do{
  45. if(check()) {ans++;
  46. for(int i=1;i<=2*n;i++)
  47. cout<<a[i]<<" ";
  48. cout<<endl;
  49. }
  50. }while(next_permutation(a+1,a+1+2*n));
  51. printf("ANS=%d\n",ans);
  52. }*/
  53. scanf("%d%d",&n,&P);
  54. doprime();
  55. for(int i=;i<=prime_num;i++){
  56. long long s=;
  57. for(int j=*n;j/=prime[i];) s+=j;
  58. // cout<<"s1="<<s<<endl;
  59. for(int j=n;j/=prime[i];) s-=j;
  60. //cout<<"s2="<<s<<endl;
  61. for(int j=n+;j/=prime[i];) s-=j;
  62. // cout<<"s3="<<s<<endl;
  63. ans=1ll*ans*qpow(prime[i],s)%P;
  64. }
  65. // cout<<"Okprime"<<endl;
  66. /* for(int i=1;i<=prime_num;i++)
  67. cout<<prime[i]<<" ";cout<<endl;*/
  68. /* for(int i=1;i<=2*n;i++)
  69. mulfz(i);
  70. for(int i=1;i<=n;i++)
  71. mulfm(i);
  72. for(int i=1;i<=n+1;i++)
  73. mulfm(i);*/
  74. /* cout<<"OKfenjie"<<endl;
  75. for(int i=1;i<=prime_num;i++)
  76. ans=1ll*ans*qpow(prime[i],fz[i]-fm[i])%P;
  77. cout<<"Okqpow"<<endl;*/
  78. printf("%d",ans);
  79. return ;
  80. }

这道题取模,下道题高精。

HNOI2009有趣的数列的更多相关文章

  1. BZOJ 1485: [HNOI2009]有趣的数列( catalan数 )

    打个表找一下规律可以发现...就是卡特兰数...卡特兰数可以用组合数计算.对于这道题,ans(n) = C(n, 2n) / (n+1) , 分解质因数去算就可以了... -------------- ...

  2. BZOJ 1485: [HNOI2009]有趣的数列 [Catalan数 质因子分解]

    1485: [HNOI2009]有趣的数列 Description 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ai}: (2)所 ...

  3. BZOJ_1485_[HNOI2009]有趣的数列_卡特兰数

    BZOJ_1485_[HNOI2009]有趣的数列_卡特兰数 Description 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ ...

  4. 【BZOJ1485】[HNOI2009]有趣的数列(组合数学)

    [BZOJ1485][HNOI2009]有趣的数列(组合数学) 题面 BZOJ 洛谷 题解 从小往大填数,要么填在最小的奇数位置,要么填在最小的偶数位置. 偶数位置填的数的个数不能超过奇数位置填的数的 ...

  5. [HNOI2009]有趣的数列 题解(卡特兰数)

    [HNOI2009]有趣的数列 Description 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ai}: (2)所有的奇数项满 ...

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

    题面:[HNOI2009]有趣的数列 题解: 观察到题目其实就是要求从长为2n的序列中选n个放在集合a,剩下的放在集合b,使得集合a和集合b中可以一一对应的使a中的元素小于b. 2种想法(实质上是一样 ...

  7. 「BZOJ1485」[HNOI2009] 有趣的数列 (卡特兰数列)

    「BZOJ1485」[HNOI2009] 有趣的数列   Description 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ai ...

  8. 洛谷P3200 [HNOI2009]有趣的数列(Catalan数)

    P3200 [HNOI2009]有趣的数列 题目描述 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ai}: (2)所有的奇数项满足 ...

  9. bzoj1485: [HNOI2009]有趣的数列(Catalan数)

    1485: [HNOI2009]有趣的数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 2105  Solved: 1117[Submit][Stat ...

  10. [HNOI2009]有趣的数列(卡塔兰数,线性筛)

    [HNOI2009]有趣的数列 题目描述 我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件: (1)它是从1到2n共2n个整数的一个排列{ai}: (2)所有的奇数项满足a1< ...

随机推荐

  1. SAP-参数(条件表)配置教程–GS01/GS02/GS03

    转载:http://www.baidusap.com/abap/others/2849 在SAP开发中,某段代码运行可能需要满足某个条件,通常解决办法有两种:一种是在代码中写死限制条件,此种方式当限制 ...

  2. 面试常考HTTP协议知识点

    协议简介 1. 应用层协议, 一般以TCP为基础,数据收发通过TCP实现: 2. 一次性连接.服务器与客户端的每次连接只处理一个请求,下次请求重新建立连接: 3. 无状态协议.服务器不保留与客户交易时 ...

  3. 戴尔 R730 服务器装 server 2016。

    服务器里面原装系统是 centos 7 ,服务器快递过来时 我没用root账号,(主要是主管忘了这台服务器用户名密码了). 需要用的系统是 server 2016 我这边就按正常的装机流程装机. 1, ...

  4. CentOS7.X基于LAMP环境搭建cacti

    1.搭建好LAMP环境 2.登录数据库创建cacti create database cacti default character set utf8; 创建cacti的账户密码 grant all ...

  5. java后端处理高并发

    一个登陆页面可能会被很多账户同时登陆或者注册,那么我们就好处理这些并发,否则降低程序的使用率,甚至程序奔溃,下面一段代码处理程序的高并发效果不错. /** *@author xiaoxie *@dat ...

  6. Google的三大马车

    Google的三大马车Google fs + Map Reduce + Big Table 开源Java实现HDFS Hadoop Hbase 云盘实现用廉价的服务器提供与万级的数据库存储①廉价的服务 ...

  7. Luogu P1445[Violet]樱花/P4167 [Violet]樱花

    Luogu P1445[Violet]樱花/P4167 [Violet]樱花 真·双倍经验 化简原式: $$\frac{1}{x}+\frac{1}{y}=\frac{1}{n!}$$ $$\frac ...

  8. 3星|路骋《用得上的商学院》:100个MBA知识点的简单介绍

    作者在序言中说,放弃了上亿的股票期权去念了两年全脱产的清华-MIT Global MBA.念完后认为课程不错,考虑到这种课本科毕业不能直接念,工作几年后又很难脱产来念,因此办了一个音频课程来讲这个MB ...

  9. js事件冒泡/捕获

  10. 基于JS的高级脚本语言 Sara

    Sara-基于JS的高级脚本语言 欢迎使用Sara,Sara是一款基于JavaScript的全新的高级脚本语言! Sara不像我们工作室上一款编程语言作品-Ginit一样,他属于更高级的语言 Sara ...