FHQ Treap 主要通过 merge \operatorname{merge} merge 和 spilt \operatorname{spilt} spilt 两个核心操作和其他辅助函数来实现。
- 本文用 l c x lc_x lcx 表示 x x x 的左儿子, r c x rc_x rcx 表示 x x x 的右儿子, d a t x dat_x datx 表示 x x x 的随机优先级
merge
- merge ( x , y ) \operatorname{merge}(x,y) merge(x,y) 要求 x x x 所在子树中的元素均小于等于 y y y 所在子树中的元素,将两棵树合并,并返回合并后的根节点。
- 实现方法:
- 若 d a t x < d a t y dat_x<dat_y datx<daty,则 x x x 作为新根节点, y y y 与 r c x rc_x rcx 合并后的根节点作为新的 r c x rc_x rcx。
- 若 d a t x ≥ d a t y dat_x \ge dat_y datx≥daty,则 y y y 作为新根节点, x x x 与 l c y lc_y lcy 合并后的根节点作为新的 l c y lc_y lcy。
- 若出现空节点,则不需合并,返回 x + y x+y x+y 即可。
inline int merge(int x,int y){
if(!x||!y)return x+y;
pushdown(x),pushdown(y);
if(tr[x].dat<tr[y].dat){
tr[x].rc=merge(tr[x].rc,y);
pushup(x);
return x;
}
else{
tr[y].lc=merge(x,tr[y].lc);
pushup(y);
return y;
}
}
spilt
按权值分裂
- spilt ( u , v , x , y ) \operatorname{spilt}(u,v,x,y) spilt(u,v,x,y) 表示将 u u u 中 ≤ v \le v ≤v 的元素合并到 x x x 中,将 > v >v >v 的元素合并到 y y y 中。由于用返回值处理比较麻烦,通常通过引用实现。
- 实现方法:
- 若 v a l u ≤ v val_u \le v valu≤v,则 u u u 及其左子树中所有元素都 ≤ v \le v ≤v,都应划分到 x x x 中,继续分裂 r c rc rc 到 r c rc rc 和 y y y 中。
- 若 v a l u > v val_u>v valu>v,则 u u u 及其右子树中所有元素都 > v >v >v,都应划分到 y y y 中,继续分裂 l c lc lc 到 x x x 和 l c lc lc 中。
- 若 u u u 为空节点,则不需要分裂,令 x ← 0 x \gets 0 x←0, y ← 0 y \gets0 y←0,返回即可。
inline void spilt(int u,int v,int &x,int &y){
if(!u){x=y=0;return;}
pushdown(u);
if(tr[u].val<=v)
x=u,spilt(tr[u].rc,v,tr[u].rc,y);
else
y=u,spilt(tr[u].lc,v,x,tr[u].lc);
pushup(u);
}
按排名分裂
- 相同道理。
inline void spilt(int u,int k,int &x,int &y){
if(!u){x=y=0;return;}
pushdown(u);
if(k<=tr[tr[u].lc].siz)
y=u,spilt(tr[u].lc,k,x,tr[u].lc);
else
x=u,spilt(tr[u].rc,k-tr[tr[u].lc].siz-1,tr[u].rc,y);
pushup(u);
}