【BZOJ 1078】 1078: [SCOI2008]斜堆

1078: [SCOI2008]斜堆

Description

  斜堆(skew heap)是一种常用的数据结构。它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值
都比它父亲大。因此在整棵斜堆中,根的值最小。但斜堆不必是平衡的,每个结点的左右儿子的大小关系也没有任
何规定。在本题中,斜堆中各个元素的值均不相同。 在斜堆H中插入新元素X的过程是递归进行的:当H为空或者X
小于H的根结点时X变为新的树根,而原来的树根(如果有的话)变为X的左儿子。当X大于H的根结点时,H根结点的
两棵子树交换,而X(递归)插入到交换后的左子树中。 给出一棵斜堆,包含值为0~n的结点各一次。求一个结点
序列,使得该斜堆可以通过在空树中依次插入这些结点得到。如果答案不惟一,输出字典序最小的解。输入保证有
解。

Input

  第一行包含一个整数n。第二行包含n个整数d1, d2, ... , dn, di < 100表示i是di的左儿子,di>=100表示i
是di-100的右儿子。显然0总是根,所以输入中不含d0。

Output

  仅一行,包含n+1整数,即字典序最小的插入序列。

Sample Input

6
100 0 101 102 1 2

Sample Output

0 1 2 3 4 5 6

HINT

Source

 

 

【分析】

  这题要懂斜堆才能做。。

考虑斜堆中最后插入的那个结点,容易发现:
(1)它一定是一个极左结点(就是从根往它的路上一直都是沿着左链走),因为插入的时候每次都是插入到左子树中;
(2)它一定木有右子树,因为插入的时候每次都是把原来的某棵子树作为新结点的左子树;

满足(1)(2)的结点可能有多个,但紧接着可以发现,这个斜堆中的每个结点如果木有左子结点,那么也木有右子结点(或者说,每个非叶结点都有左子树),而在插入一个结点之前,其所有的祖先都被交换了左右子树,所以,若新结点的祖先中有满足(1)(2)的,且新结点不是叶结点,那么在新结点插入之前,这个满足(1)(2)的祖先必然是只有右子树而木有左子树的,这与上面的那个性质矛盾,所以,可以得出:最后插入的那个结点一定是满足(1)(2)的结点中,深度最小的那个(设为X),除非X的左子结点是叶结点,此时为了满足字典序最小,应该取X的左子结点为最后插入的。找到这个最后插入的结点以后,只需要把它删掉,并把它的所有祖先交换左右子树,就是插入该结点以前的状态了。这样可以找到字典序最小的插入顺序。

ORZ大神:http://www.cppblog.com/MatoNo1/archive/2013/03/03/192131.html

 

证明:斜堆中的每个结点如果木有左子结点,那么也木有右子结点:因为右儿子是由左儿子旋转得到的,而在旋转的同时左边一定被插入节点了。

 

  说得很清楚明白了,数据很小,直接暴力。

 

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 #define Maxn 110
 8 
 9 int a[Maxn];
10 
11 struct node
12 {
13     int x,lc,rc,f;
14 }t[2*Maxn];
15 
16 int op[Maxn],rt;
17 
18 int ffind()
19 {
20     int x=rt,nw;
21     while(t[x].rc) x=t[x].lc;
22     if(t[x].lc!=0&&t[t[x].lc].lc==0&&t[x].lc>x) nw=t[x].lc;
23     else nw=x;
24     
25     if(nw==rt) rt=t[nw].lc,t[nw].f=0;
26     else
27     {
28         t[t[nw].f].lc=t[nw].lc;
29         t[t[nw].lc].f=t[nw].f;
30         x=t[nw].f;
31         while(1) 
32         {
33             swap(t[x].lc,t[x].rc);
34             if(x==rt) break;
35             x=t[x].f;
36         }
37     }
38     return nw;
39 }
40 
41 int main()
42 {
43     int n;
44     scanf("%d",&n);
45     op[0]=0;
46     for(int i=0;i<n;i++) t[i].lc=t[i].rc=0;
47     t[0].f=0;
48     for(int i=1;i<=n;i++)
49     {
50         int x;
51         scanf("%d",&x);
52         if(x>=100) t[x-100].rc=i,t[i].f=x-100;
53         else t[x].lc=i,t[i].f=x;
54     }
55     rt=0;
56     for(int i=0;i<=n;i++)
57     {
58         op[i]=ffind();
59     }
60     for(int i=n;i>=0;i--) printf("%d ",op[i]);
61     return 0;
62 }
View Code

 

2017-01-17 15:00:26

posted @ 2017-01-17 14:54  konjak魔芋  阅读(280)  评论(0编辑  收藏  举报