全局平衡二叉树

· · 个人记录

全局平衡二叉树可以代替复杂度较劣的树剖 + 线段树,在某些树上维护信息的问题中做到一个 \log 的复杂度。

首先将原树重链剖分,将结点编号换成 dfs 序,这样重链编号就连续了。对每个点 i,记 sn_i 为重儿子,sz_i 为子树大小,w_i=sz_i-sz_{sn_i} 为一个权值。对于每一条重链,建出一个二叉树的结构,做法是找到当前区间的带权中点(第一个满足 w 的前缀和乘 2 大于 w 的总和的点),将这个点作为根,递归左右区间。对于一条重链,设链头为 i,建出的二叉树的根为 j,则新树上 j 的父亲为原树上 i 的父亲。

我们称二叉树上的边为实边,ji 的父亲连的边为虚边。根据树剖的性质,一个点跳到根只会跳 \log n 次虚边。记 sum 为新树上当前点子树内和当前点属于同一重链的 w_i 之和,若跳实边则 sum 至少翻倍,若跳虚边则 sum 不会减少。根据 w_i 的定义,一条重链的 w_i 之和为链头的子树大小,所以 sum 至多达到 n。于是新树的树高为 \log n 级别。

这样建出的新树就是常见的全局平衡二叉树,不过这样的结构维护复杂信息不太方便,需要支持二叉搜索树上的区间定位。考虑改造成 leafy 的,就是递归左右区间的过程,将 [l,m-1],[m+1,r],改成 [l,m],[m+1,r]。对于虚边就是二叉树的根,向链头父亲对应的另一棵二叉树的叶子连边。这样结点数会变成 2n,不过可以用类似 zkw 线段树 + 标记永久化的方式维护信息(对每条重链自顶向下递归 + 下传标记也是可以的,不过常数大一些)。

链修改链查询是容易的,直接向树剖一样从两个端点向上跳重链,在某一个重链的二叉树上时向 zkw 线段树一样向上跳即可。再加上子树修改子树查询就比较麻烦,以 P3384 【模板】轻重链剖分/树链剖分 为例,大概要维护两种永久化标记,一种只作用于当前重链区间 [l,r] 内的点,另一种作用于当前重链区间 [l,r] 内的点和这些点的轻子树,如果信息比区间加区间查询更复杂的话可能就不太容易维护了。代码先咕了。