FHQ Treap 主要通过 merge\operatorname{merge}merge 和 spilt\operatorname{spilt}spilt 两个核心操作和其他辅助函数来实现。
- 本文用 lcxlc_xlcx 表示 xxx 的左儿子,rcxrc_xrcx 表示 xxx 的右儿子,datxdat_xdatx 表示 xxx 的随机优先级
merge
- merge(x,y)\operatorname{merge}(x,y)merge(x,y) 要求 xxx 所在子树中的元素均小于等于 yyy 所在子树中的元素,将两棵树合并,并返回合并后的根节点。
- 实现方法:
- 若 datx<datydat_x<dat_ydatx<daty,则 xxx 作为新根节点,yyy 与 rcxrc_xrcx 合并后的根节点作为新的 rcxrc_xrcx。
- 若 datx≥datydat_x \ge dat_ydatx≥daty,则 yyy 作为新根节点,xxx 与 lcylc_ylcy 合并后的根节点作为新的 lcylc_ylcy。
- 若出现空节点,则不需合并,返回 x+yx+yx+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) 表示将 uuu 中 ≤v\le v≤v 的元素合并到 xxx 中,将 >v>v>v 的元素合并到 yyy 中。由于用返回值处理比较麻烦,通常通过引用实现。
- 实现方法:
- 若 valu≤vval_u \le vvalu≤v,则 uuu 及其左子树中所有元素都 ≤v\le v≤v,都应划分到 xxx 中,继续分裂 rcrcrc 到 rcrcrc 和 yyy 中。
- 若 valu>vval_u>vvalu>v,则 uuu 及其右子树中所有元素都 >v>v>v,都应划分到 yyy 中,继续分裂 lclclc 到 xxx 和 lclclc 中。
- 若 uuu 为空节点,则不需要分裂,令 x←0x \gets 0x←0,y←0y \gets0y←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);elsey=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);elsex=u,spilt(tr[u].rc,k-tr[tr[u].lc].siz-1,tr[u].rc,y);pushup(u);
}
例题
【模板】文艺平衡树