優點:**比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 ...