旋轉還是無旋?treap從入門到想死

2021-08-14 05:06:00 字數 3764 閱讀 7972

優點:**比splay好打

缺點:很多操作(尤其是區間操作)不資磁

插入方式和二叉查詢樹一致,新建節點後,要給節點rand乙個pos值。

然後返回改節點的父親,如果該節點pos值比父親大,就要旋轉,以保證父節點的pos值小於兒子的,滿足堆的性質。使用隨機數可以使得這棵樹盡量平衡。

void

ins(

int&x,ll bh)

if(bh)else

}

treap的旋轉操作比splay清爽多了,可以說是相當於splay的zig/zag操作。

void

spin

(int

&x,int is)

例題:洛谷p2234/bzoj2234/codevs1296 營業額統計

ll pre

(int x,ll num)

ll nxt

(int x,ll num)

## 刪除

找到要刪除的節點x,看它的左右兒子,旋轉pos值較小的那個兒子,直到x成為葉子節點,刪除x。

void

del(

int&x,ll bh)

if(pos[son[x][0

]]]]

)spin

(x,0),

del(x,bh)

;else

spin

(x,1),

del(x,bh);}

if(bh)del

(son[x][0

],bh)

;else

del(son[x][1

],bh)

;}

優點:可以可持久化,資磁的操作多

缺點:比splay慢

例題:洛谷p2464/codevs1840

打這道題的時候,我真的是比小j還煩惱…這道題有一種解法是離散+對於每一種書建立一棵無旋treap,然後進行操作。

無旋treap是基於合併與**兩個操作的一種treap。

現在我們有兩棵分別以a和b為根的treap,想要a在左b在右的合併。如何合併呢?

首先,比較a和b的pos值,較小的那個可以作為乙個根,然後將其左/右子樹和另乙個合併。如果是a,那麼讓a的右子樹和b合併。如果是b,那麼讓b的左子樹和a合併。

int

merge

(int a,

int b)

else

}

現在我們想在以x為根的子樹裡,左邊**出乙個num大小的樹。怎麼辦?

我們的函式返回值是乙個pair,這樣可以存兩棵**出來的子樹的根:左邊的和右邊的。

首先特判兩種可以直接**的情況:

1.x的左子樹大小為num,此時可以直接**為左子樹和以x為根的子樹

2.x的左子樹大小為num-1,此時可以直接**為以x為根的子樹和右子樹

然後再比較x左子樹和num的大小,進行遞迴**。(不懂看**)

#define pr pair

#define mkp make_pair

pr split

(int x,

int num)

if(sz[son[x][0

]]+1

==num)

if(num]]

)else

}

有了上面兩種操作,插入操作就變得輕鬆了許多,即新建乙個節點,找到原樹中該節點應該排在第幾,然後**原來的樹,再依次合併(**出來的左邊,新節點,**出來的右邊)

插入乙個區間也與之類似。

**:見完整**標記處。

找到要刪除的節點,先**出該節點左邊一棵子樹,再**出該節點右邊一棵子樹,然後把兩棵子樹合併,就不要那個節點了。

刪除乙個區間也與之類似。

**:見完整**標記處。

用類似於刪除的方法把整個區間取出來,打上區間操作標記即可。

然後在合併和刪除操作的時候都要記得pushdown。

例題就是bzoj3223/洛谷p3391 文藝平衡樹 啦,完整**下面有

小j的煩惱

#include

using

namespace std;

intread()

#define pr pair

#define mkp make_pair

const

int n=

600005

;int n,m,cnt,sss;

map<

int,

int> mp;

int rt[n]

,bk[n]

,son[n][2

],pos[n]

,wz[n]

,sz[n]

;int

newjd

(int x)

voidup(

int x)

intmerge

(int a,

int b)

else

}pr split

(int x,

int num)

if(sz[son[x][0

]]+1

==num)

if(num]]

)else

}int

find

(int x,

int kth)

intmain()

while

(m--

)else

}return0;

}

文藝平衡樹

#include

using

namespace std;

#define mkp make_pair

#define pr pair

const

int n=

100005

;int n,m,rt;

int son[n][2

],rev[n]

,pos[n]

,sz[n]

;voidup(

int x)

voidpd(

int x)

intmerge

(int a,

int b)

else

}pr split

(int x,

int num)

if(sz[son[x][0

]]+1

==num)

if(num]]

)else

}int

build

(int l,

int r)

int mid=

(l+r)

>>1;

return

merge

(build

(l,mid)

,build

(mid+

1,r));

}void

print

(int x)

intmain()

print

(rt)

;return0;

}

既然學了無旋treap,就不得不去做一做這道神題啦!

你會發現無旋treap雖然比splay慢,可是編碼速度更快呢!

戳我看**

總結 無旋treap

顧名思義就是沒有旋轉操作的treap.還是很好打的.畢竟旋轉操作旋轉上天.兩個核心操作 split和merge split是將一棵樹分成兩棵樹的操作.注意這裡的要求是對於確定的樹,將其前k個點分成新樹,剩下的點變成另一顆新樹,因此可能出現多個切割的地方.對於乙個節點來說,我們必然只會處理它的一顆子樹...

無旋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 ...