學習筆記 左偏樹入門

2022-03-13 09:22:34 字數 2679 閱讀 4791

主要是整理自己想出來的幾個梗

吶,下面進入正題。左偏樹,一種可以合併的堆狀結構,支援\(insert/remove/merge\)等操作。穩定的時間複雜度在\(\theta(\log n)\)的級別。對於乙個左偏樹中的節點,需要維護的值有\(dist\)和\(value\)。其中\(value\)不必多說,\(dist\)記錄這個節點到它子樹裡面最近的葉子節點的距離,葉子節點距離為\(0\)。

首先,他有以下幾個喜聞樂見的性質:

那麼這就可以推出以下性質:

\(emmm​\)這可是乙個很美妙的性質啊。

這是整個左偏樹的重頭戲,時間複雜度穩定在乙個\(log\),其主要思想就是不斷把新的堆合併到新的根節點的右子樹中——因為我們的右子樹決定「距離」這個變數,而距離又一定保證在\(~\log~\)的複雜度內,所以不斷向右子樹合併。

大體思路(以小根堆為例),首先我們假設兩個節點\(x\)和\(y\),\(x\)的根節點的權值小於等於\(y\)的根節點(否則\(swap(x,y)\)),把\(x\)的根節點作為新樹\(z\)的根節點,剩下的事就是合併\(x\)的右子樹和\(y\)了。

合併了\(x\)的右子樹和\(y\)之後,\(x\)當\(x\)的右子樹的距離大於\(x\)的左子樹的距離時,為了維護性質二,我們要交換\(x\)的右子樹和左子樹。順便維護性質三,所以直接\(dist_x = dist_+1\).

inline int merge(int x, int y)
我們觀察,我們是不斷交替拆分右子樹,由推論可得我們的距離不會大於$\theta(\log(n_x+1))+\theta(\log(n_y+1))-2 =o(\log n_x+ \log n_y) $

這個地方比較喜聞樂見的是需要存\(root\),即需要路徑壓縮。不路徑壓縮的話,尋一次\(rt\)就是\(\theta(n)\)的了,複雜度是不對的但似乎luogu的模板,不路徑壓縮會更快

……\(pop\)的話,亂搞就好了\(233\)

inline void pop(int x)
然後就是總**:

#include #include #define maxn 150010

#define swap my_swap

#define ls s[x].son[0]

#define rs s[x].son[1]

using namespace std ;

struct trees[maxn] ; int n, t, a, b, c, i ;

inline int merge(int x, int y) ;

int my_swap(int &x, int &y)

inline int get(int x)

inline void pop(int x)

inline int merge(int x, int y)

int main()

else

} return 0 ;

}

問題大概就是路徑壓縮……

\(luogup3377\)

很不負責任地處了資料,導致以下這份**可以過:

using namespace std ;

struct trees[maxn] ;

int n, t, a, b, c, i ;

inline int merge(int x, int y) ;

int my_swap(int &x, int &y)

int get(int x)

inline void pop(int x)

inline int merge(int x, int y)

int main()

else

}return 0 ;

}

一切都很正常,但問題在於他複雜度不對:

int get(int x)
這顯然是個上界為\(o(n)\)的函式……不寒而慄……

所以他是不對的,這組資料可以很好的卡掉(由巨佬小粉兔製作)。

所以應該用乙個並查集維護。而我們在路徑壓縮之後,必須要在\(pop\)後,給\(pop\)掉的點乙個指標指向新的根,所以:

inline int get(int x)

inline void pop(int x)

於是最後的**:

#include #include #define maxn 150010

#define swap my_swap

#define ls s[x].son[0]

#define rs s[x].son[1]

using namespace std ;

struct trees[maxn] ; int n, t, a, b, c, i ;

inline int merge(int x, int y) ;

int my_swap(int &x, int &y)

inline int get(int x)

inline void pop(int x)

inline int merge(int x, int y)

int main()

else

} return 0 ;

}

\(\rm\)

左偏樹學習筆記

左偏樹是一種基於二叉樹的可並堆。定義乙個節點的 距離 dis xdis x disx 為它到空節點的最短路長度,左偏樹強制要求 dis lson dis rson dis ge dis dislso n d isrs on 所以 dis x di srso n 1dis x dis 1 disx d...

左偏樹 學習筆記

首先要知道左偏樹是用來幹什麼的。如果給我們兩個優先序列,然後讓我把這兩個優先佇列合併成乙個優先佇列。如果直接用堆,就是將乙個佇列裡面的數不斷彈出然後扔到另乙個佇列裡。複雜度是 o n n為佇列中數的個數。但是用左偏樹就可以做到 log n 1 n 2 ps 為了便於討論,本文所有的左偏樹均已小根樹為...

學習筆記 左偏樹

左偏樹是一種可並堆,除了堆的基本功能,最大的特點就是支援合併堆,甚至比普通堆好寫。下面敘述以小根堆為例,大根堆對稱。o log n 求乙個數所在堆的根 o 1 求最小值 o log n 合併兩個堆 o log n 刪除最小值 o log n 插入乙個數 n 是插入的總節點數 或當前堆的節點數 str...