真正的危机不是机器人像人一样思考,而是人像机器一样思考。 ——凉宫春日的忧郁

[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(){;}
View Code

 

posted @ 2017-09-09 10:30  Hzoi_Mafia  阅读(118)  评论(0编辑  收藏  举报
我们都在命运之湖上荡舟划桨,波浪起伏着而我们无法逃脱孤航。但是假使我们迷失了方向,波浪将指引我们穿越另一天的曙光。 ——死神