[SCOI2008]斜堆

时间:2021-01-01 15:58:24

题目大意

1.题目描述

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

2.样例文件:

input1:
6
100 0 2 102 4 104
output1:
4 6 5 2 0 1 3
..............................................................
input2:
7
0 100 1 102 2 3 5
output2:
2 5 0 3 4 6 7 1

思路及解法:

本题的数据范围出到\(n \leq 5 \times 10^3\)是肯定没问题的。
所以总结一下,本题时间复杂度\(O(n^2)\),空间复杂度\(O(n)\),思维复杂度\(O(n!)\)....
.
言归正传,这题到底怎么做?
由于每次都是插入左子树中,所以就会有一些神奇的事情。
我们考虑 最后一个插入的点 会有什么性质:
(1)性质1:它是一个极左节点(从根到它的路径都是走左子树)
(2)性质2:它一定没有右子树
显然满足上述条件的点有很多,那么仔细观察后可以发现:
性质3:对于树上任意一个点,如果它没有左子树,则它一定没有右子树
.
然后就可以搞一波事情了。
我们假设最后加入的点为 \(X\) ,如果它的祖先中存在点 \(Y\) 也满足性质\(1,2\),则:
<1>
若\(X\)不为叶子节点:
由于\(X\)插入\(Y\)的子树中时会反转\(Y\)的左右儿子,
那么\(Y\)的当前左子树中包含\(X\),而现在\(Y\)又没有右子树,
所以在插入前,\(Y\)只有右子树,没有左子树。
这显然与性质3矛盾,即这种情况不可能发生。
所以最后插入的点一定是 深度最浅的满足性质1,2的节点,设它为 \(del1\)。
<2>
若\(X\)为叶子节点:
那么即插入\(X\)前\(Y\)没有儿子。
所以这种情况显然是合法的,
即如果\(del1\)有左儿子\(del2\),且\(del2\)为叶子节点,那么\(del2\)也是合法的。
.
综上所述,最后插入的点可能为:
(1)深度最浅的满足性质1,2的节点
(2)深度最浅的满足性质1,2的节点的左儿子,前提是这个左儿子为叶子节点。
由于我们要保证字典序,所以存在(2)情况时我们则优先选叶子节点。
所以我们依照这个原则不断删点、修改。
最后把答案数组倒着输出即可(P.s :建议把所有点的编号都加\(1\)再处理)。

实现代码:

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define mx 60
#define ll long long
using namespace std;

int n,root,fa[60],ls[60],rs[60],ans[60];

IL void Work(){
    for(RG int sq = 1; sq <= n; sq ++){
        RG int del1 = 0 , del2 = 0, x = root;
        while(x){ if(!rs[x]){del1 = x; break;} x = ls[x]; }
        if(!rs[ls[x]] && !ls[ls[x]] && ls[x])
            del2 = ls[x];
        if(del2){ans[sq] = del2; ls[del1] = 0; }
        else {
            ans[sq] = del1;
            ls[fa[del1]] = ls[del1]; fa[ls[del1]] = fa[del1];
        }
        if(!del2 && del1 == root)root = ls[del1] , fa[root] = 0;;
        RG int ff = fa[(del2)?del2 : del1];
        while(ff)
            swap(ls[ff],rs[ff]) , ff = fa[ff];
    }return;
}

int main(){
    cin >> n; n++; root = 1;
    for(RG int i = 2,f; i <= n; i ++){
        cin >> f;
        if(f >= 100){f-=100; f++; rs[f] = i; fa[i] = f;}
        else f++ , ls[f] = i , fa[i] = f;
    }
    Work();
    for(RG int i = n; i >= 1; i --)ans[i] --;
    for(RG int i = n; i >= 1; i --)cout<<ans[i]<<" ";
    return 0;
}