线段树与离线询问
线段树与离线询问结合的问题在 OI 领域也有出现.这种技巧又被称为线段树分治.
假如你需要维护一些信息,这些信息会在某一个时间段内出现,要求在离线的前提下回答某一个时刻的信息并,则可以考虑使用线段树分治的技巧.
实际上线段树分治常有以下用途:
- 用原本不支持删除但是支持撤销的数据结构来模拟删除操作.如朴素的并查集无法高效支持删边操作.
- 不同属性的数据分别计算.如需要求出除了某一种颜色外,其他颜色数据的答案.
如果大家现在不明白没有关系,这两种用途我们都会在例题中阐述.
过程
首先我们建立一个线段树来维护时刻,每一个节点维护一个 vector 来存储位于这一段时刻的信息.
插入一个信息到线段树中和普通线段树的区间修改是类似的.
然后我们考虑如何处理每一个时间段的信息并.考虑从根节点开始分治,维护当前的信息并,然后每到一个节点的时候将这个节点的所有信息进行合并.回溯时撤销这一部分的贡献.最后到达叶子节点时的信息并就是对应的答案.
如果更改信息的时间复杂度为 𝑂(𝑇(𝑛))O(T(n)),可以通过设置一个栈保留更改,以 𝑂(𝑇(𝑛))O(T(n))
的时间复杂度撤销.撤销不维持均摊复杂度.
整个分治流程的总时间复杂度是 𝑂(𝑛log𝑛(𝑇(𝑛) +𝑀(𝑛)))O(nlogn(T(n)+M(n))) 的,其中 𝑂(𝑀(𝑛))O(M(n))
为合并信息的时间复杂度,空间复杂度为 𝑂(𝑛log𝑛)O(nlogn)
.
实现
---|---
## 例题
[luogu P5787 二分图/【模板】线段树分治](https://www.luogu.com.cn/problem/P5787)
你需要维护一个 𝑛n 个点 𝑚m 条边的无向图.第 𝑖i 条边为 (𝑥𝑖,𝑦𝑖)(xi,yi),出现的时刻为 [𝑙𝑖,𝑟𝑖)[li,ri),其余时刻消失.
对于每一个时刻,若此时该图为二分图,输出 `Yes`,否则输出 `No`.
解题思路
使用种类并查集来维护一个图是否是二分图,然后就可以套用线段树分治了.
注意可撤销的并查集不能路径压缩,只能按秩合并.
参考代码
---|---
颜色限制 restriction
一个 𝑛n 点 𝑚m
边的无向图,有 𝑘k
种颜色编号为 0 ∼𝑘 −10∼k−1
,每条边有一种颜色.
对于每种颜色,请判断假如删去所有这种颜色的边,得到的图是否连通?是否是一棵树?
输出满足删去后图连通的颜色数和删去后图是树的颜色数.
解题思路
对于每一种颜色,建立一个时间,在这个时间内没有这个颜色的边,其他边都有.用一个并查集维护一下即可.
参考代码
---|---
[luogu P4219 [BJOI2014] 大融合](https://www.luogu.com.cn/problem/P4219)
需要维护一个 𝑛n 个点的森林,初始时是散点.
有 𝑞q 个操作,支持:
* `A x y` 连边 (𝑥,𝑦)(x,y).
* `Q x y` 输出经过边 (𝑥,𝑦)(x,y) 的路径数.
允许离线.
解题思路
考虑允许离线,因此可以想到线段树分治.
然后考虑如何支持 Q 操作.如果不存在 (𝑥,𝑦)(x,y) 这条边,答案就是 𝑥x 所在连通块大小乘上 𝑦y 所在连通块大小.可以用并查集维护.
因此你可以将 Q 拆成三个时间 𝑘 −1,𝑘,𝑘 +1k−1,k,k+1.其中 𝑘 −1k−1 是这条边的终止时刻,𝑘 +1k+1 是这条边的起始时刻.这样 𝑘k 就没有这条边,正好回答询问.
参考代码
---|---
[luogu P2056 [ZJOI2007] 捉迷藏](https://www.luogu.com.cn/problem/P2056)
出一个 𝑛n 个点的树,每个点有黑白两种颜色.初始时每个点都是黑色的.𝑞q
次操作,支持:
C x将第 𝑥x个点的颜色反转.
G询问树上两个黑色点的最远距离.特别地,若不存在黑色点,输出 −1−1.
允许离线.
解题思路
首先考虑如何维护树上点集的直径,有下面的推论:
对于一个集合 𝑆S和只有一个点的集合 {𝑃}{P}
.若集合 𝑆S
的直径为 (𝑈,𝑉)(U,V)
.则点集 𝑆 ∩{𝑃}S∩{P}
的直径只可能为 (𝑈,𝑉),(𝑈,𝑃)(U,V),(U,P)
或 (𝑉,𝑃)(V,P)
.
然后考虑解决原问题.我们可以考虑维护黑色点集,维护每一个点在黑色点集中的若干个时间段(具体你开一个桶记录一下上一次进入黑色点集的时刻即可).
然后就自然地想到离线,将所有时间刻插入到线段树中.然后在线段树上分治,每次线段树上的点会记录当前时间段点集新增的点,新增点可以使用上面的推论,找到新点集直径的两个端点.
撤销是平凡的,开一个栈记录一下直径端点的变化即可.
参考代码
---|---
## 习题
* [CF601E A Museum Robbery](https://codeforces.com/problemset/problem/601/E) 线段树分治 + 背包 dp.
* [CF19E Fairy](https://codeforces.com/problemset/problem/19/E) 线段树分治 + 种类并查集.
* [luogu P5227 [AHOI2013] 连通图](https://www.luogu.com.cn/problem/P5227) 线段树分治 + 并查集.
* [luogu P4319 变化的道路](https://www.luogu.com.cn/problem/P4319) 线段树分治 + Link Cut Tree 维护最小生成树.
* [luogu P3733 [HAOI2017] 八纵八横](https://www.luogu.com.cn/problem/P3733) 线段树分治 + 线性基.
**本页面部分内容参考自博文[Deleting from a data structure](https://cp-algorithms.com/data_structures/deleting_in_log_n.html),版权协议为 CC-BY-SA 4.0.**
* * *
> __本页面最近更新: 2026/1/7 08:56:54,[更新历史](https://github.com/OI-wiki/OI-wiki/commits/master/docs/topic/segment-tree-offline.md)
> __发现错误?想一起完善?[在 GitHub 上编辑此页!](https://oi-wiki.org/edit-landing/?ref=/topic/segment-tree-offline.md "edit.link.title")
> __本页面贡献者:[Tiphereth-A](https://github.com/Tiphereth-A), [xiezheyuan](https://github.com/xiezheyuan), [c-forrest](https://github.com/c-forrest), [Enter-tainer](https://github.com/Enter-tainer), [aofall](https://github.com/aofall), [CoelacanthusHex](https://github.com/CoelacanthusHex), [Great-designer](https://github.com/Great-designer), [Kensuke-Hinata](https://github.com/Kensuke-Hinata), [Marcythm](https://github.com/Marcythm), [Persdre](https://github.com/Persdre), [shuzhouliu](https://github.com/shuzhouliu), [TOMWT-qwq](https://github.com/TOMWT-qwq)
> __本页面的全部内容在**[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.zh) 和 [SATA](https://github.com/zTrix/sata-license)** 协议之条款下提供,附加条款亦可能应用