FHQ

FHQ 满足 \(val_{{ls}_{u}} \le val_u \le val_{{rs}_{u}}\)

对于每一个节点 \(u\) , 我们维护 \(sz_u\)\(val_u\) 以及 \(rd_u\) (随机的 \(key\) ) .

  1. \(val\) 分裂:

考虑维护两个指针,分别代表两课树,先判断 \(val_u\) 的大小与 \(v\) 的关系,然后向对应方向分裂。
具体地,如果 $val_u < v $ 那么把 \(u,ls_u\) 划入 \(x\) ,向\(rs_u\) 递归;相反,就向 \(ls_u\) 递归。

inline void spl(int u,int v,int &x,int &y){
	if(!u) return x=y=0,void();
	if(t[u].val<=v) spl(t[u].r,v,t[x=u].r,y);
	else spl(t[u].l,v,x,t[y=u].l);
	up(u);
}

2.按排名分裂:

类比按 \(val\) 分裂,对于 \(sz_{ls_u}\) 分讨即可。

3.合并

需要满足 \(\forall u \in x , v \in y 有 val_u \le val_v\)
否则,我们只能进行启发式合并 \(O(log^2 n)\).

既然我们确定了 \(val\) 的关系,就只要考虑 \(key\) 的大小. 按照 \(key\) 的大小确定根节点,递归分讨即可。

inline int merge(int x,int y){
	if(!x || !y) return x + y;
	if(t[x].rd<t[y].rd) return t[y].l=merge(x,t[y].l),up(y),y;
	return t[x].r=merge(t[x].r,y),up(x),x;
}

对于其他找 \(rank,pre,kth\) 等操作:直接在树上找或者利用分裂合并找答案。

一般地,在实现中,写 \(val < v\) 而不是 \(val \le v\).

posted @ 2025-08-06 11:22  mysterys  阅读(10)  评论(0)    收藏  举报