總結 無旋treap

2022-06-03 21:12:12 字數 1568 閱讀 8470

顧名思義就是沒有旋轉操作的treap.

還是很好打的.

畢竟旋轉操作旋轉上天.

兩個核心操作:splitmerge

split是將一棵樹分成兩棵樹的操作.

注意這裡的要求是對於確定的樹,將其前k個點分成新樹, 剩下的點變成另一顆新樹,因此可能出現多個切割的地方.

對於乙個節點來說,我們必然只會處理它的一顆子樹,因此用遞迴去找處理的子樹就行了.

返回時對於每乙個點更新一下它被處理的那顆子樹.

函式的返回值是兩顆子樹的根.

具體看**吧

define pii pairdefine mp make_pair

pii split(int rt, int k)

else

return tmp;

}

merge即是將兩顆樹合併的操作, 注意這裡合併的樹(a,b)要求max_value(a) < min_value(b),這樣把ab一左一右相接(即保證b的每乙個節點都在a的右邊)便保證了權值的有序,我們就只要維護堆的性質了.(顯然split分出來的兩顆樹就滿足這樣的性質)

int merge(int ra, int rb) 

else

}

這裡是題目.

單點操作基本都能靠merge+split完成.

比如這題只需加上splay中一樣的getkth(找到第k個數), findkth(找到數a的位置),

那麼:insert=getkth(findkth+getkth)+split+merge

delete=getkth(findkth+getkth)+split+merge

單點插入刪除也可用(merge)(split)完成.

這裡是題目.

其實和單點操作沒什麼區別...

區間的插入刪除也是使用(merge)(split)完成.

刪除好說,但是注意插入時需要我們先建好一顆子樹再merge.

於是又有了乙個build函式.

我們可以乙個乙個把點插到新樹中去(一開始有一顆空樹).

那麼每次插入的點必然在樹的最右端.

然後開始維護小根堆的性質.

考慮root -> right son -> right son ... 這樣一條鏈, 我們先把新點接在這條鏈最下面,

然後找到其中深度最小的乙個key值大於大於點的節點,把以它為根的子樹當做新點

的左子樹, 然後用新點代替它原來的位置就可以了.(相當於把新點沿著鏈一直向上旋)

但是需要注意排布在這條鏈上的樹是沒有維護(pushup/update)過的, 因此每次

尋找到要被移到新點下面的點都需要一次pushup, 最後再給仍在鏈上的點來一發pushup.

因為每次加入的點都在鏈上, 可以證明每個點都會(在它的所有子樹之後)經過一次pushup

還有乙個需要注意的點是splay中的虛點.

無旋treap並不需要虛點,但是在pushup的時候可能考慮到空子樹的情況,為避免空子樹的影響

需要乙個初始化.

無旋treap學習小記

高一才學這麼基本的平衡樹,退役了 鑑於旋轉treap不能可持久化,與splay相比除了常數小以外沒有什麼不同,所以就不學了。treap tree heap,即二叉搜尋樹 堆 它的中序遍歷是有序的,這是二叉搜尋樹的性質。且對於每乙個點有乙個隨機的鍵值,對於整個樹的任意一棵子樹,鍵值滿足堆的性質。基於隨...

Fhq Treap無旋Treap練習

洛谷p3369 模板 普通平衡樹 split採用按權 author revolia submit includeusing namespace std typedef long long ll const int maxn 1e6 5 int l maxn r maxn val maxn size ...

無旋treap的初步學習

是一種詭異的平衡樹 最主要的操作 就是split 拆分成兩棵子樹 和merge 合併為一棵子樹 這個。就不講了吧。因題而異。所以我寫出來是為了什麼?分為兩種 一種是以前k個和剩下的來拆分,適用於區間操作時 r1為前k個的子樹的根,r2為剩下的子樹的根 void split treap int x,i...