首先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)的,就当常数大了吧)。

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

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
int n,P,/*a[20],*/ans=;
/*bool check(){
for(int i=1;i<=n;i++)
if(a[i*2-1]>a[i*2]) return 0;
for(int i=3;i<=n*2;i++)
if(a[i]<a[i-2]) return 0;
return 1;
}*/
int prime[],prime_num;
bool v[];
void doprime(){
for(int i=;i<=*n+;i++){
if(!v[i]) prime[++prime_num]=i;
for(int j=;j<=prime_num&&i*prime[j]<=*n+;j++){
v[prime[j]*i]=;
if(i%prime[j]==) break;
}
}
}
int qpow(int x,int k){
int val=;
for(;k;k>>=,x=1ll*x*x%P)
if(k&) val=1ll*val*x%P;
return val%P;
}
int main(){
//打表找规律系列。。。
/* while(1){
ans=0;
scanf("%d%d",&n,&P);
for(int i=1;i<=(n<<1);i++)
a[i]=i;
do{
if(check()) {ans++;
for(int i=1;i<=2*n;i++)
cout<<a[i]<<" ";
cout<<endl;
}
}while(next_permutation(a+1,a+1+2*n));
printf("ANS=%d\n",ans);
}*/
scanf("%d%d",&n,&P);
doprime();
for(int i=;i<=prime_num;i++){
long long s=;
for(int j=*n;j/=prime[i];) s+=j;
// cout<<"s1="<<s<<endl;
for(int j=n;j/=prime[i];) s-=j;
//cout<<"s2="<<s<<endl;
for(int j=n+;j/=prime[i];) s-=j;
// cout<<"s3="<<s<<endl;
ans=1ll*ans*qpow(prime[i],s)%P;
}
// cout<<"Okprime"<<endl;
/* for(int i=1;i<=prime_num;i++)
cout<<prime[i]<<" ";cout<<endl;*/
/* for(int i=1;i<=2*n;i++)
mulfz(i);
for(int i=1;i<=n;i++)
mulfm(i);
for(int i=1;i<=n+1;i++)
mulfm(i);*/
/* cout<<"OKfenjie"<<endl;
for(int i=1;i<=prime_num;i++)
ans=1ll*ans*qpow(prime[i],fz[i]-fm[i])%P;
cout<<"Okqpow"<<endl;*/
printf("%d",ans);
return ;
}

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

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. 《阿里巴巴 Java 开发规约》自动化检测插件安装及体验

    2017 开春之际,有助于提高行业编码规范化水平的<阿里巴巴 Java 开发手册>首次面世.汇聚阿里集团近万名技术精英的经验知识,这套高含金量的手册一经公开,便引起业界普遍关注和学习. 历 ...

  2. openstack-neutron(2)

    VXLAN 独立于底层的网络拓扑:反过来,两个 VTEP 之间的底层 IP 网络也独立于 VXLAN.VXLAN 数据包是根据外层的 IP header 路由的,该 header 将两端的 VTEP ...

  3. jQuery遍历(2)

    上期我们讲了遍历的祖先和后代的问题,现在我们讲讲遍历同胞 同胞拥有相同的父元素. 通过 jQuery,您能够在 DOM 树中遍历元素的同胞元素. jQuery siblings() 方法 siblin ...

  4. enumerateKeysAndObjectsUsingBlock 的用法

    block是一个有序列的指令代码块,通常在代码中间花括号括起来的objective-c代码,但是它能被传递和被分配局部变量,然后作为一个参数来传递,基本上可以把花括号中间的代码保存到数据结构里.看起来 ...

  5. kubernetes 应用快速入门

    使用kubectl进行增.删.查.改等常用操作 查看kubectl命令帮助 kubectl -h kubectl controls the Kubernetes cluster manager. Fi ...

  6. RT-Thread--线程间通信

    线程中通信 在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取,根据读取到的全局变量值执行相应的动作,达到通信协作的目的: ...

  7. VS Code Monokai Pro验证

    最新的VS Code Monokai Pro激活方式 需要vscode,并且安装了monokai pro插件 进入目录 Mac OS cd -/.vscode/extensions/monokai.t ...

  8. Mac上使用brew安装Nginx服务器

    使用brew安装nginx $ brew install nginx 启动nginx sudo nginx 访问localhost:8080 发现已出现nginx的欢迎页面了.  为方便期间,做个软 ...

  9. 解决在macOS下安装了python却没有pip命令的问题【经验总结】

    可以使用brew直接安装python,但是安装完成了之后没有pip命令. pip是常用的python包管理工具,类似于java的maven.第一反应brew install pip,却提示没这货. 可 ...

  10. THINKPHP SQL注入处理方式

    //注入的产生一般都是对用户输入的参数未做任何处理直接对条件和语句进行拼装. //不安全的写法举例1 $_GET['id']=8;//希望得到的是正整数 $data=M('Member')->w ...