顧名思義就是沒有旋轉操作的treap.
還是很好打的.
畢竟旋轉操作旋轉上天.
兩個核心操作:split和mergesplit是將一棵樹分成兩棵樹的操作.
注意這裡的要求是對於確定的樹,將其前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...