ZJOI2010 Perm 排列计数
Posted Forever_goodboy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ZJOI2010 Perm 排列计数相关的知识,希望对你有一定的参考价值。
[ZJOI2010]Perm 排列计数
时间限制: 1 Sec 内存限制: 259 MB题目描述
称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
输入
输入文件的第一行包含两个整数 n和p,含义如上所述。
输出
输出文件中仅包含一个整数,表示计算1,2,?, ???的排列中, Magic排列的个数模 p的值。
样例输入
20 23
样例输出
16
提示
100%的数据中,1 ≤ ??? N ≤ 106, P??? ≤ 10^9,p是一个质数。 数据有所加强
solution:
建一颗二叉树(因为只用判断i和i/2的关系),直接上公式:f[size[rt]]=C(size[rt]-1,size[ls(rt)])*f[size[ls(rt)]]*f[size[rt]-1-size[ls(rt)]];,利用组合数直接可以推出来,然后利用类似树规跑一遍就好了。不过好像有线性的方法,本菜鸟不会呵呵。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 #define ls(x) (x<<1) 7 #define rs(x) (x<<1|1) 8 #define root 1 9 int n,mod,size[4*1000005]; 10 long long inv[1000005],f[1000005]; 11 long long qpow(long long x,int t) { 12 long long ans=1; 13 for( ; t; ans=(t&1)?(ans*x*1ll%mod):(ans),t>>=1,x=(x*x*1ll)%mod) ; 14 return ans; 15 } 16 void init() { 17 inv[0]=1; 18 for(int i=1; i<=1000000; ++i) { 19 inv[i]=(inv[i-1]*i)%mod; 20 } 21 } 22 int C(int n,int m) { 23 return inv[n]*qpow(inv[m],mod-2)%mod*qpow(inv[n-m],mod-2)%mod; 24 } 25 void dfs(int x) { 26 if(x>n) { 27 return ; 28 } 29 size[x]=1; 30 dfs(ls(x)); 31 dfs((rs(x))); 32 size[x]+=size[ls(x)]+size[rs(x)]; 33 } 34 void dfs2(int rt) { 35 if(rt>n) { 36 return ; 37 } 38 dfs2(ls(rt)); 39 dfs2(rs(rt)); 40 if(f[size[rt]]) { 41 return ; 42 } 43 if(size[rt]==1) { 44 f[0]=f[1]=1; 45 return ; 46 } 47 f[size[rt]]=C(size[rt]-1,size[ls(rt)])*f[size[ls(rt)]]%mod*f[size[rt]-1-size[ls(rt)]]%mod; 48 } 49 int main() { 50 //freopen("permzj.in","r",stdin); 51 //freopen("permzj.out","w",stdout); 52 scanf("%d%d",&n,&mod); 53 init(); 54 dfs(root); 55 dfs2(root); 56 printf("%d",f[size[root]]); 57 return 0; 58 }
以上是关于ZJOI2010 Perm 排列计数的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ2111[ZJOI2010]Perm 排列计数 组合数
组合数学+lucas定理+逆元 BZOJ2111 [ZJOI2010]Perm 排列计数