1. 程式人生 > >[SCOI2008]斜堆

[SCOI2008]斜堆

esp 大於 clu 解法 相同 復雜度 times 題目 反轉

題目大意

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;
}

[SCOI2008]斜堆