线段树 分裂、合并

约定

线段树的分裂合并默认是操作 动态开点值域线段树,用 \(V\) 表示线段树的值域,用 \(a\)\(b\) 表示两颗较小的树(即分裂得到的两颗子树或需要合并的两棵树)的大小(值域线段树的大小定义为叶结点的数量),用 \(a_{1\sim k}\) 表示多棵较小的树的大小

线段树分裂

给定一个 \(x\),把值域为 \([1,V]\) 的线段树分裂为值域 \([1,x]\)\((x,V]\) 的两棵树

单次分裂的时间复杂度为 \(O(\log V)\)

线段树合并

把两棵 值域相交 的线段树合并为一棵

单次时间复杂度 \(O(\min(a,b)\log V)\)

若合并多棵线段树,则总时间复杂度为 \(O(\sum a_i\log V)\),优于启发式合并的 \(O(\sum a_i\log\sum a_i\log V)\)

若值域不交,则时间复杂度为 \(O(\log V)\) 的,实现方式类似 \(\text{fhq treap}\)\(\text{merge}\) 操作

例 1:P5327 [ZJOI2019] 语言

给定一棵 \(n\) 个结点的树和 \(m\) 条链,求有多少无序点对 \((u,v)\;(u\ne v)\),满足存在一条链同时包含两者,\(n,m\le10^5\)

考虑对于每个 \(u\) 求出包含它的合法点对数量,答案即为 \(n\) 个点对应数量之和除以 \(2\)

包含 \(u\) 的合法 \((u,v)\) 的数量为经过 \(u\) 的所有链的并的大小减一

若干条链的并即为包含所有端点的最小连通子树

包含 \(a_{1\sim k}\) 的最小连通子树的大小为 \(\frac12(\text{dis}(a_1,a_k)+\sum_{i=1}^{k-1} \text{dis}(a_i,a_{i+1}))\),其中 \(a_{1\sim k}\)\(\text{dfn}\) 排序

\(\text{dfs}\) 给定树时,维护包含当前处理结点的所有链的端点的可重集

通过树上差分,问题转化为向集合中加入数、从集合中删除数、查询集合中相邻点的距离之和(假设集合按 \(\text{dfn}\) 排序,统计答案时要加上第一个和最后一个的距离并乘上系数)、合并集合(要将处理子树时的集合与处理其父亲时的集合合并)

这容易通过动态开点权值线段树实现,其中线段树的 \(p\) 下标存储 \(\text{dfn}\)\(p\) 的点的出现次数,非叶结点存储相邻点距离之和(平衡树也可以,且其空间复杂度线性)

若使用 \(O(n\log n)-O(1)\) \(\text{lca}\),则可以做到 \(O(n\log n)\);若使用 \(O(n)-O(\log n)\) \(\text{lca}\),则可以做到 \(O(n\log^2 n)\)(假设 \(n,m\) 同级),空间复杂度 \(O(n\log n)\)

代码

例 2:T158644 [QwQOI2020] III

给定一个长为 \(n\) 的排列,\(m\) 次操作,区间升序 / 降序排序,或查询某一位置的值,\(n,m\le10^5\)

考虑颜色段均摊,有序的子段为一个颜色段,每个子段建立一棵权值线段树

对于查询某一位置的操作,找到所在段后线段树上二分即可

对于区间排序的操作,则将两端的段分裂(由于每段内有序,因此分裂成两半等同于按给定值分裂),并将中间的所有段合并为一个新段

时间复杂度 \(O(n\log^2n)\)(假设 \(n,q\) 同阶),空间复杂度 \(O(n\log n)\)

代码

参考

  1. \(\text{2024.12.13 EZDS.pdf\;\;\; by Luzhuoyuan}\)
posted @ 2025-04-28 16:08  Hstry  阅读(47)  评论(0)    收藏  举报