[ZJOI2010]Perm
[ZJOI2010]Perm
题目
称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值
INPUT
输入文件的第一行包含两个整数 n和p,含义如上所述。
OUTPUT
输出文件中仅包含一个整数,表示计算1,2,?, ???的排列中, Magic排列的个数模 p的值。
SAMPLE
INPUT
20 23
OUTPUT
16
解题报告
这竟然是一道树规= =
其实想明白之后挺简单的
我们考虑一颗满二叉树,一个节点$i$如果有左儿子,那么它的左儿子编号一定为$i\times 2$,如果它有右儿子,那么它的右儿子编号一定为$i\times 2+1$
再回来看这道题,假如我们建一颗满二叉树,那么问题不就转化成所有儿子的权值都比父亲的权值大的方案数么?
设$f[size[i]]$代表编号为$i$的节点的方案数
我们要取出$i-1$个(把自己去掉)比它大的数,一部分放在左子树,一部分放在右子树,且当左子树确定了取出哪些数时,右子树所取出的数也是一定的
故我们可以推出状态转移方程:
$$f[size[i]]=C_{size[i]-1}^{size[i<<1]}\times f[size[i<<1]]\times f[size[i<<1|1]]$$
然后实现即可
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 typedef long long L; 6 L n,p; 7 L fac[1000005]; 8 inline L po(L x,L hm){ 9 L ret(1); 10 while(hm){ 11 if(hm&1) 12 ret=ret*x%p; 13 x=x*x%p; 14 hm>>=1; 15 } 16 return ret; 17 } 18 inline L C(int n,int m){ 19 return fac[n]*po(fac[m],p-2)%p*po(fac[n-m],p-2)%p; 20 } 21 struct edge{ 22 int e; 23 edge *n; 24 }a[1000005],*pre[1000005]; 25 int tot; 26 inline void insert(int s,int e){ 27 a[++tot].e=e; 28 a[tot].n=pre[s]; 29 pre[s]=&a[tot]; 30 } 31 int size[2000005]; 32 inline void get_size(int u){ 33 size[u]=1; 34 for(edge *i=pre[u];i;i=i->n){ 35 int e(i->e); 36 if(!size[e]){ 37 get_size(e); 38 size[u]+=size[e]; 39 } 40 } 41 } 42 L f[2000005]; 43 inline void dfs(int u){ 44 if(u>n){ 45 // size[u]=0; 46 // f[0]=1; 47 return; 48 } 49 // cout<<u<<endl; 50 // size[u]=1; 51 dfs(u<<1); 52 // size[u]+=size[u<<1]; 53 dfs(u<<1|1); 54 // size[u]+=size[u<<1|1]; 55 f[size[u]]=C(size[u]-1,size[u<<1])*f[size[u<<1]]%p*f[size[u<<1|1]]%p; 56 } 57 inline int gg(){ 58 // freopen("permzj.in","r",stdin); 59 // freopen("permzj.out","w",stdout); 60 scanf("%lld%lld",&n,&p); 61 fac[0]=fac[1]=1; 62 for(int i=2;i<=n;++i){ 63 L tmp(i); 64 while(tmp%p==0) 65 tmp/=p; 66 fac[i]=fac[i-1]*tmp%p; 67 } 68 for(int i=1;i<=n;++i){ 69 if((i<<1)>n) 70 break; 71 insert(i,i<<1); 72 if((i<<1|1)>n) 73 break; 74 insert(i,i<<1|1); 75 } 76 get_size(1); 77 f[0]=f[1]=1; 78 dfs(1); 79 printf("%lld",f[n]%p); 80 // for(int i=1;i<=n;++i) 81 // cout<<"i="<<i<<" size[i]="<<size[i]<<endl; 82 return 0; 83 } 84 int K(gg()); 85 int main(){;}