AIGC标识 一些使用技巧

默认读者已经熟练掌握:线段树各种标记、树状数组、主席树、FHQ Treap、树链剖分、点分治、普通莫队、CDQ 分治、LCT 的基本操作。本讲不讲这些的实现,只讲它们背后的原理,以及在这些原理之上再往上走一层的东西。
组织方式不按结构名,按六个原理。每个原理先讲"为什么成立",再讲"能推出什么结构",最后讲"哪些题在考这个原理"。
建议课时:6 课时。


0. 总纲:六个原理

数据结构题的难度不在于"知道多少种结构",而在于"看出题目在考哪个原理"。竞赛中所有数据结构技巧,几乎都落在下面六条之一或其组合:

原理 一句话 典型产物
I. 信息是幺半群,标记是作用 能合并就能分治,能复合就能延迟 线段树一切变体、动态 DP、历史最值、猫树
II. 势能与均摊 单次可能很慢,但总量有上界 Beats、颜色段均摊、线段树合并、DSU on tree、Splay
III. 分治的三个方向 下标、时间、值域,离线后都可以分治 CDQ、线段树分治、整体二分、二进制分组
IV. 可逆与可持久 能撤销就能回溯,能复制路径就能保留历史 可撤销并查集、可持久化一切、版本差分
V. 阈值与平衡 两种代价之积恒定时,取平方根 分块、根号分治、莫队及其二次离线、KD-tree
VI. 树到序列的映射 树上问题的难点只是"怎么线性化" 重链 / 长链 / 实链剖分、点分树、DFS 序族

读题时的检查顺序:信息能不能合并(I)→ 操作有没有势能(II)→ 能不能离线(III)→ 需不需要历史版本(IV)→ 有没有两种代价可以平衡(V)→ 树怎么变成序列(VI)。


I. 信息是幺半群,标记是作用

I.1 为什么线段树能维护一个信息

线段树的每个节点存的是"这段区间的信息",pushup 是"两段相邻区间的信息 \(\to\) 合并后区间的信息"。这要求信息集合 \(M\) 上有一个二元运算 \(\circ\) 满足结合律:

\[(a \circ b) \circ c = a \circ (b \circ c) \]

有结合律的 \((M, \circ)\) 叫半群;再加一个单位元就是幺半群。线段树能维护的信息,恰好是半群。 不需要交换律(左右顺序是固定的),不需要逆元。

信息 \(\circ\) 是半群? 备注
区间和 \(+\) 是 还是交换群
区间最值 \(\max\) 是 幂等
区间 gcd \(\gcd\) 是 幂等
最大子段和 \((s, l, r, a)\) 见下 是 四元组,不交换
区间哈希 \((h, \text{len})\) \(h_L B^{\text{len}_R} + h_R\) 是 不交换
矩阵 矩阵乘 是 不交换,万能
区间"括号匹配后剩余" \((\text{左多余}, \text{右多余})\) 抵消 是
区间中位数 无 否 两段的中位数推不出整体,必须用值域结构

最大子段和:\((s_1,l_1,r_1,a_1)\circ(s_2,l_2,r_2,a_2) = (s_1+s_2,\ \max(l_1, s_1+l_2),\ \max(r_2, r_1+s_2),\ \max(a_1,a_2,r_1+l_2))\)。

判断一个信息能否用线段树维护的方法:写出"两段合并"的公式。写得出来且只依赖两段各自的信息,就能维护。写不出来(如中位数、众数),就要换一个"更大的信息"(如整个值域直方图,那就是值域线段树 / 主席树),或者换原理(II、V)。

I.2 标记是"作用",复合要有结合律

懒标记 \(t\) 是一个函数 \(M \to M\)(把区间信息映成打了标记后的信息)。多个标记先后作用,需要能复合成一个标记,且复合满足结合律。这就是说标记集合 \(T\) 在复合下也是一个幺半群,且 \(T\) 作用在 \(M\) 上。

"区间乘 \(a\) 再加 \(b\)"的标记是仿射函数 \(x \mapsto ax + b\),复合 \((a_1, b_1)\) 后 \((a_2, b_2)\) 得 \((a_1 a_2,\ a_2 b_1 + b_2)\)。这个复合有结合律,所以是合法标记。

标记要作用在整段信息上,所以标记的定义需要包含"如何作用于区间和"(用到区间长度)。这就是为什么线段树节点常要存长度。

I.3 交换律决定能否永久化

标记永久化(不下传,查询时累计路径标记)要求路径上的标记顺序可以任意交换。所以:

  • 加法标记:可交换,可永久化。可持久化线段树的区间加、二维线段树、树套树的内层都靠这个。
  • 赋值标记:不可交换,不能永久化(后来的赋值必须覆盖先前的)。
  • 乘加标记:不可交换,不能永久化。

推论:可持久化线段树支持区间加、区间和,但不支持区间赋值(除非改用"每个版本一棵新的根到叶路径"的技巧,代价更大)。

I.4 历史最值:把"时间"编进标记

问题(洛谷 P6242 / P4314 CPU 监控):区间加,区间查询"历史上出现过的最大值"。

设每个位置 \(x\) 的当前值 \(v\)、历史最大值 \(h\)。区间加 \(a\) 后 \(v' = v + a\),\(h' = \max(h, v + a)\)。

标记定义为 \((a, b)\):当前加 \(a\),"从标记生效起到现在这段时间里的最大前缀加量"为 \(b\)。作用:\(v' = v + a\),\(h' = \max(h, v + b)\)。

复合:先 \((a_1, b_1)\) 后 \((a_2, b_2)\):

\[v'' = v + a_1 + a_2,\qquad h'' = \max\big(h,\ v + b_1,\ v + a_1 + b_2\big) \]

所以

\[\boxed{(a_1, b_1)\circ(a_2, b_2) = \big(a_1 + a_2,\ \max(b_1,\ a_1 + b_2)\big)} \]

单个加法 \(a\) 对应标记 \((a, a)\)(\(a>0\))或 \((a, 0)\)(历史最大不会因为减少而更新,所以 \(b = \max(a, 0)\))。

这个复合满足结合律(验证一下:本质是 \((\max, +)\) 半环上的 \(2\times 2\) 上三角矩阵乘法)。历史最值不是一个"技巧",是一个新的标记幺半群。 只要写出这个复合,剩下的就是普通线段树。

历史最小值、历史和(\(\sum\) 每时刻的值)同理,后者的标记是 \((a, \text{累计加量} \cdot \text{经过的时间})\) 的复合,需要额外记录"标记覆盖的时间长度"。

I.5 矩阵:万能标记与动态 DP

当信息是一个向量、转移是线性的(在某个半环上),标记就可以用矩阵表示,复合就是矩阵乘。

\((\max, +)\) 半环:把加法换成 \(\max\),乘法换成 \(+\),矩阵乘法 \(C_{ij} = \max_k (A_{ik} + B_{kj})\)。结合律成立,所以"DP 转移"可以当作矩阵来合并。

动态 DP(洛谷 P4719):树上最大权独立集,带单点修改点权。

普通 DP:\(f_u^0 = \sum_v \max(f_v^0, f_v^1)\),\(f_u^1 = w_u + \sum_v f_v^0\)。

重链剖分后,\(u\) 的儿子分成一个重儿子 \(h\) 和若干轻儿子。把轻儿子的贡献单独记为

\[g_u^0 = \sum_{v \text{ 轻}}\max(f_v^0, f_v^1),\qquad g_u^1 = w_u + \sum_{v\text{ 轻}} f_v^0 \]

则 \(f_u\) 只依赖 \(f_h\) 和 \(g_u\):

\[\begin{pmatrix} f_u^0 \\ f_u^1\end{pmatrix} = \begin{pmatrix} g_u^0 & g_u^0 \\ g_u^1 & -\infty\end{pmatrix} \otimes \begin{pmatrix} f_h^0 \\ f_h^1\end{pmatrix} \]

一条重链自顶向下是 \(u_1, u_2, \dots, u_k\)(\(u_k\) 是叶),链顶的 \(f\) 等于 \(G_{u_1}\otimes G_{u_2}\otimes\cdots\otimes G_{u_k}\otimes(0, 0)^T\)。用线段树维护重链上矩阵的乘积。

修改点 \(u\) 的权值:

  1. 更新 \(G_u\),线段树单点修改,重新算出 \(u\) 所在重链链顶的 \(f\)。
  2. 链顶 \(t\) 的父亲 \(p\) 的 \(g_p\) 依赖 \(f_t\)(\(t\) 是 \(p\) 的轻儿子),把旧的 \(f_t\) 从 \(g_p\) 中减掉,加上新的,更新 \(G_p\)。
  3. 对 \(p\) 所在重链重复。

每次修改经过 \(O(\log n)\) 条重链,每条链一次线段树操作,\(O(\log^2 n)\),矩阵 \(2\times 2\) 常数 8。

全局平衡二叉树:把每条重链不建普通线段树,而是按子树大小加权建带权平衡的二叉树(类似 Splay 的静态形态,但不旋转),并把各链的树用轻边连起来。整棵结构深度 \(O(\log n)\),修改只需从叶向上更新一遍,严格 \(O(\log n)\)。这是"静态化的 LCT",是动态 DP 的最优实现。

动态 DP 的适用范围:任何"树上 DP,状态数为常数 \(k\),转移是 \((\oplus, \otimes)\) 半环上的线性变换"的问题,包括最大独立集、最小点覆盖、最小支配集、路径计数、带权匹配等。矩阵是 \(k \times k\)。

I.6 非交换半群的 \(O(1)\) 区间查询:猫树

ST 表只能处理幂等的信息(\(\max\)、gcd)。若信息只是半群(如最大子段和、矩阵乘),且无修改,如何 \(O(1)\) 查询?

猫树:建线段树,对每个节点 \([l, r]\)(中点 \(m\))预处理

  • \(\text{suf}[i] = a_i \circ a_{i+1}\circ\cdots\circ a_m\),\(i \in [l, m]\)
  • \(\text{pre}[j] = a_{m+1}\circ\cdots\circ a_j\),\(j \in [m+1, r]\)

查询 \([x, y]\):找到 \(x, y\) 被分到不同儿子的那个节点(用叶子编号的最高相异位定位,\(O(1)\)),答案 \(= \text{suf}[x]\circ\text{pre}[y]\)。

预处理 \(O(n\log n)\) 次 \(\circ\),查询 \(1\) 次 \(\circ\)。代价:不支持修改。

分治 + 半群 的推广:这个"按中点劈开、两边各存前后缀"的思想也是分治 NTT、分治线段树优化 DP 的骨架。

I.7 信息依赖"左边的统计量":非标准 pushup

楼房重建(洛谷 P4198):区间内"前缀最大值的个数"。右子区间的贡献依赖左子区间的最大值。

pushup 时对右子树做一次 \(O(\log n)\) 的递归(每层只走一边),单次修改 \(O(\log^2 n)\)。原理:右子树的贡献函数 \(c(\text{lim})\) 关于门槛 \(\text{lim}\) 单调,且可以在树上二分。

同类问题:区间内"能看到的点数"、"单调栈大小"、"以 \(a_i\) 结尾的最长上升前缀",凡是右边的答案是左边某个单调量的函数、且这个函数能在线段树上递归求值的,都是这个模型。

I.8 Kinetic Segment Tree(KTT,选讲)

维护一组一次函数 \(y_i = k_i x + b_i\),操作:区间对 \(x\) 加正数(时间推进),区间查询 \(\max y_i\)。

每个节点存当前最大的那条线,以及"最快会被超越的时刻"(下一次最优线变化的 \(x\))。区间加 \(x\) 时若不超过这个时刻,直接打标记;否则递归重建。势能分析给出 \(O(n\log^2 n)\) 或 \(O(n\log^3 n)\) 均摊。这是 Beats(原理 II)与半群维护的结合体。例题:洛谷 P5693 [CSA] Kinetic Tournament。

I.9 本节题单

题 考什么
洛谷 P4513 小白逛公园 最大子段和半群
洛谷 P4314 CPU 监控 历史最值标记复合
洛谷 P6242 线段树 3 历史最值 + Beats
洛谷 P4719 / P4751 动态 DP 矩阵 + 重链 / 全局平衡二叉树
洛谷 P5024 [NOIP2018] 保卫王国 动态 DP 应用(强制选 / 不选)
洛谷 P4198 楼房重建 非标准 pushup
CF 718C Sasha and Array 矩阵标记
洛谷 P6240 好吃的题目 猫树 / 分治

II. 势能与均摊

II.1 势能分析的模板

定义势能函数 \(\Phi(\text{状态}) \ge 0\)。一次操作的均摊代价 $= $ 实际代价 \(+ \Delta\Phi\)。若每次操作的均摊代价都 \(\le c\),则总实际代价 \(\le qc + \Phi_0 - \Phi_{\text{end}} \le qc + \Phi_0\)。

用法:找一个量,它"每次昂贵操作都会显著减少,而每次普通操作只增加常数"。

结构 势能 每次昂贵操作减少 结论
并查集(路径压缩 + 按秩) 略(Tarjan) \(O(\alpha)\)
Splay \(\sum \log \text{size}\) \(O(\log n)\) 均摊
线段树合并 总节点数 每递归一层减少一个节点 总 \(O(n\log n)\)
DSU on tree 略 每个点被加入 \(O(\log)\) 次 \(O(n\log n)\)
区间开方 \(\sum \log\log a_i\) 每个非 1 元素至少减一 \(O(n\log\log V)\) 次叶子访问
区间取模 \(\sum \log a_i\) 取模后至少减半 \(O(n\log V)\)
颜色段均摊 段数 区间赋值删掉 \(k\) 段只新增 \(O(1)\) 段 总 \(O(n + q)\) 段变化
Beats(chmin) 区间内不同值的个数之和 每次穿透次大值都合并两个值 \(O((n+q)\log n)\)

II.2 Segment Tree Beats 的完整版本与证明

操作:区间 chmin、区间 chmax、区间加、查询区间和 / 最大 / 最小 / 历史最大。

节点信息:\(\text{mx1}, \text{mx2}, \text{cmx}\)(最大值、严格次大值、最大值个数),\(\text{mn1}, \text{mn2}, \text{cmn}\),\(\text{sum}\),历史最大 \(\text{hmx}\)。

标记:对"最大值那一类"的加法 \(\text{tmx}\)、对"最小值那一类"的加法 \(\text{tmn}\)、对"其余元素"的加法 \(\text{tot}\),以及各自的历史最大版本 \(\text{htmx}, \text{htmn}, \text{htot}\)(用 I.4 的 \((a,b)\) 复合)。

下传时的关键判断:子节点的最大值是否等于父节点的最大值?相等则子节点的最大值那一类接收 \(\text{tmx}\),否则接收 \(\text{tot}\)。最小值同理。注意当 \(\text{mx1} = \text{mn1}\)(区间只有一种值)或 \(\text{mx2} = \text{mn1}\) 之类的重叠情况要特判。

chmin \(v\) 的递归条件:

  • \(v \ge \text{mx1}\):无事。
  • \(\text{mx2} < v < \text{mx1}\):只有最大值那一类变,打标记。
  • \(v \le \text{mx2}\):递归。

复杂度证明(只有 chmin / chmax):定义每个节点的势能为"该节点区间内不同值的个数",\(\Phi = \sum_{\text{节点}}\)。初始 \(\Phi = O(n\log n)\)。每次递归到"\(v \le \text{mx2}\)"的节点,操作后原本 \(\ge \text{mx2}\) 的至少两个不同值合并为一个,该节点势能至少减 1。递归的总次数 \(\le\) 势能总减少量 \(+\) 正常的 \(O(\log n)\) 定位。所以总 \(O((n + q)\log n)\)。

加上区间加:区间加会让一个节点的势能增加(原本相等的值可能因为部分被加而分开),一次区间加影响 \(O(\log n)\) 个节点,每个节点势能最多增加 \(O(\log n)\)(可以证明),所以总 \(O((n+q)\log^2 n)\)。实际跑起来接近单 \(\log\)。

教学要点:Beats 不是"记住 mx2 < v 就打标记",而是"找一个势能,让每次深入递归都消耗它"。区间开方、取模、按位与或(下面)都是同一个模板。

II.3 区间按位或 / 按位与 + 区间加 + 区间和

问题(洛谷 P2572 / CF 1004F 类):区间 or \(v\)、and \(v\)、加、求和。

节点维护 \(\text{and}\)、\(\text{or}\)。区间 or \(v\) 时:

  • 若 \((\text{and} \,\&\, v) = (\text{or}\,\&\, v)\),即 \(v\) 中为 1 的每一位,在区间内所有数上要么全 0 要么全 1:那么 or \(v\) 等价于加上 \(v \,\&\, \sim\text{and}\)(那些全 0 的位),打加法标记。
  • 否则递归。

势能:区间内"不一致的位"的个数(\(\text{or} \oplus \text{and}\) 的 popcount)之和。每次递归至少使某一位从不一致变成一致,且区间加最多让 \(O(\log V)\) 位变不一致。总 \(O((n+q)\log n\log V)\)。

II.4 颜色段均摊("珂朵莉树"的正确打开方式)

用 std::set 维护值相同的极长段 \([l, r, v]\)。区间赋值:split 出 \(l\) 和 \(r+1\) 两个断点(新增至多 2 段),删掉中间所有段,插入 1 段。

势能 = 段数。初始 \(\le n\),每次操作至多 \(+3\),删除的段数被势能支付。所有操作的遍历段数总和 \(O(n + q)\)。

这意味着:只要题目的修改是"区间赋值",任何"对区间内每一段做点什么"的操作都是均摊 \(O((n+q)\cdot \text{单段代价})\)。 这是一个严格结论,不依赖数据随机。

数据随机时,即使没有区间赋值(如区间加),随机的区间赋值出现频率足够高,段数期望 \(O(\log n)\),所以 ODT 才能"乱搞"过去。

例题:CF 896C(原题)、洛谷 P8146(区间赋值 + 区间查询,配合线段树维护段内信息)。

II.5 线段树合并与线段树分裂的势能

合并:递归只在两棵树都有的节点上进行,每次递归后两个节点合成一个,总节点数减一。初始节点数 \(O(n\log n)\),所以总合并代价 \(O(n\log n)\)。

注意:如果合并后还要保留原来的两棵树(如可持久化线段树合并),复杂度分析仍然成立,但空间需要新开节点,空间 \(O(n\log n)\) 不变(合并创建的节点数 \(\le\) 递归次数)。

分裂:每次分裂新增 \(O(\log n)\) 个节点,势能增加 \(O(\log n)\),所以"合并 + 分裂"混合的总复杂度 \(O((n + q)\log n)\)。

线段树合并维护的信息:任何幺半群信息(I.1)都可以,且合并过程中可以顺便计算跨越两棵树的贡献,例如"逆序对数"(合并时左子树 \(a\) 的计数乘右子树 \(b\) 的计数)。例题:洛谷 P3521 [POI2011] ROT-Tree Rotations。

II.6 DSU on tree 与启发式合并

启发式合并:小的合并到大的。每个元素每次被移动,所在集合大小至少翻倍,所以每个元素最多被移动 \(O(\log n)\) 次。

DSU on tree 是它在树上的特例:保留重儿子的信息,暴力加入轻儿子。每个点到根的轻边数 \(O(\log n)\),所以每个点被暴力加入 \(O(\log n)\) 次。

与线段树合并的对比:DSU on tree 只能回答"子树的离线统计",且要求信息可增删(或至少可增 + 可清空);线段树合并可以在线维护子树的整棵值域结构。

\(O(n\log^2 n)\) 的启发式合并 + 平衡树 / set:合并两个 set 时把小的逐个插入大的,总 \(O(n\log^2 n)\)。常见于"合并两个连通块的有序集合"。

II.7 Splay 与 LCT 的势能

Splay 的势能 \(\Phi = \sum_x \log \text{size}(x)\)。一次 splay 均摊 \(O(\log n)\)。LCT 的 access 均摊 \(O(\log n)\) 依赖两个事实:(1)每次 access 切换的实边数均摊 \(O(\log n)\)(重轻边论证);(2)每个 splay 均摊 \(O(\log n)\)。

实践含义:LCT 不能用 Treap 替代 Splay 来维护实链,因为 access 需要 splay 的"把访问过的节点提到根"来保证均摊。

II.8 本节题单

题 考什么
洛谷 P6242 线段树 3 Beats 完整版
洛谷 P4145 花神游历各国 开方势能
CF 438D The Child and Sequence 取模势能
洛谷 P2572 [SCOI2010] 序列操作 位运算势能(简化版)
CF 896C Willem, Chtholly and Seniorious 颜色段均摊
洛谷 P3521 ROT-Tree Rotations 线段树合并算贡献
CF 600E Lomsat gelral DSU on tree
洛谷 P5693 Kinetic Tournament KTT

III. 分治的三个方向

III.1 三种分治的统一视角

一个带修改、带查询的问题有三个维度:下标(位置)、时间(操作顺序)、值域(答案)。在线时三个维度都必须实时处理。离线后,可以选一个维度做分治,把它"降维"掉。

分治维度 名字 消掉的东西 代价
下标 / 时间(对操作序列分治) CDQ 分治 一维偏序 \(\times\log\)
时间(对操作存在区间分治) 线段树分治 删除操作 \(\times\log\)
值域(对答案分治) 整体二分 二分的在线性 \(\times\log V\)

共同点:分治后,"左半对右半的贡献"变成一个更低维的问题。CDQ 把三维偏序变成二维(一维归并、一维 BIT);线段树分治把"加边 + 删边"变成"加边 + 撤销";整体二分把"每个查询二分答案"变成"所有查询共享同一次 check"。

III.2 CDQ 分治的三个层次

层次 1:统计贡献。三维偏序:第一维排序,第二维分治归并,第三维 BIT。\(O(n\log^2 n)\)。

层次 2:优化 DP。转移 \(f_i = \max_{j<i,\ (a_j, b_j) \prec (a_i, b_i)} f_j + w_i\)。分治 \([l, r]\):先递归 \([l, m]\)(此时左半的 \(f\) 已确定),用左半更新右半,再递归 \([m+1, r]\)。顺序必须是:左、跨、右,不能像统计贡献那样"左、右、跨"。

层次 3:CDQ 套 CDQ。四维偏序:第一维排序,第二维分治,"左半对右半"的贡献变成三维偏序,再套一层 CDQ。\(O(n\log^3 n)\)。写法:内层 CDQ 处理时给每个元素打上"来自外层左半 / 右半"的标记,只统计左对右。

CDQ 的限制:贡献必须可以"左对右单向计算"且可加(BIT 维护的是可减群)。若贡献是 \(\max\) 且转移不可减,用层次 2 的 DP 写法(顺序保证不需要撤销)。

III.3 线段树分治:把删除变成撤销

适用:每个"元素"(边、点、约束)有存在区间 \([l_i, r_i]\);查询在每个时刻。要求维护的结构支持撤销(IV.1),但不需要支持删除。

经典:动态图连通性(离线)、动态二分图判定、动态最大权匹配的存在性、离线动态 MST(配合 LCT 或按秩并查集)。

不止时间轴:任何"每个元素在某个区间上有效"的维度都可以做线段树分治。例如"每条边对权值在 \([a, b]\) 范围内的询问有效",就对权值轴做线段树分治。

复杂度:每个元素挂在 \(O(\log T)\) 个节点,每个节点上的元素加入一次撤销一次,总 \(O(m\log T\cdot \text{单次操作})\)。

III.4 整体二分

适用:(1)每个查询的答案可以二分;(2)check 函数对所有查询是"同一种",可以批量做;(3)修改与查询之间只有时间顺序约束。

核心:对 \([L, R]\) 值域和一批操作,用 \(\text{mid}\) 做 check 时只考虑值 \(\le \text{mid}\) 的修改,用 BIT 累计对每个查询的贡献,把查询分到左 / 右,修改也按值分到左 / 右。每个操作在每层出现一次,共 \(\log V\) 层,每层 \(O(\text{ops}\cdot\log n)\)。

与 CDQ 的区别:CDQ 对操作序列的下标分治,整体二分对答案分治。两者可以嵌套(如整体二分内部每层用 CDQ 处理三维偏序的 check)。

III.5 二进制分组:把离线结构在线化

问题:有一个只支持"离线建立、查询"的结构 \(S\)(如 KD-tree、猫树、离线 CDQ 的结果、凸包),现在要求支持在线插入。

方法:维护若干个结构,大小分别是 \(2^{k_1} > 2^{k_2} > \cdots\)(二进制表示)。插入一个元素:新建大小 1 的结构,若已有大小 1 的,合并成 2;若已有大小 2 的,合并成 4……(二进制加法进位)。查询时对所有 \(O(\log n)\) 个结构各查一次,合并答案。

代价:每个元素被重建 \(O(\log n)\) 次,查询 \(\times\log n\)。要求:查询的答案可以从各结构的答案合并(\(\max\)、\(\sum\) 等),且结构支持"从两个小的合并 / 重建成一个大的"。

不支持删除。若要删除,配合线段树分治(III.3)或"带权删除标记"。

例题:CF 710F String Set Queries(在线 AC 自动机:二进制分组维护若干自动机)、洛谷 P4585 [FJOI2015] 火星商店问题(二进制分组 / 线段树分治 + 可持久化 Trie)。

III.6 点分治:树上的下标分治

树上"经过某点的路径"是"跨越中点的区间"的类比。重心保证递归 \(O(\log n)\) 层。

边分治:找一条边使两侧尽量平衡。原树度数无界时先"三度化"(每个点拆成一条链,新边权 0)。边分治的好处:每层只有两侧,"跨越"的贡献只需两个集合配对,某些信息(如两侧各建一棵结构)比点分治的多子树容斥好写。

点分树(IV 和 VI 的组合):把点分治的分治结构本身建成树,每层的信息用数据结构维护,从而支持修改。

III.7 本节题单

题 考什么
洛谷 P3810 三维偏序 CDQ 层次 1
洛谷 P4093 [HEOI2016] 序列 CDQ 优化 DP
洛谷 P3157 动态逆序对 CDQ / 树套树
CF 762E Radio stations CDQ 或 线段树分治
洛谷 P5787 二分图 线段树分治
洛谷 P4219 [BJOI2014] 大融合 线段树分治 / LCT
洛谷 P2617 Dynamic Rankings 整体二分
洛谷 P3332 [ZJOI2013] K 大数查询 整体二分 + 区间修改
CF 710F String Set Queries 二进制分组
洛谷 P4585 火星商店问题 线段树分治 + 可持久化 Trie
洛谷 P4292 [WC2010] 重建计划 点分治 + 单调队列

IV. 可逆与可持久

IV.1 撤销的条件

一个操作能撤销,当且仅当能在 \(O(\text{操作代价})\) 内恢复操作前的状态。做法是记录"改了什么",压栈;撤销时弹栈。

关键限制:撤销必须按栈序(后进先出)。 这意味着:

  • 线段树分治(DFS 序天然是栈序)可以用。
  • "删除任意一个早先加入的元素"不能用。

可撤销的结构:按秩合并并查集(不能路径压缩,因为路径压缩改变的节点数无界)、线段树(标记永久化或记录修改)、Trie、任何"每次操作只改 \(O(\log)\) 个位置"的结构。

不可撤销的结构:路径压缩并查集、Splay(旋转改变太多)、哈希表的重建。

IV.2 可持久化的本质:路径复制

可持久化 = 每次修改后旧版本仍可访问。对树形结构(线段树、Treap、Trie),修改只影响根到某叶的一条路径,复制这条路径(\(O(\log n)\) 个节点),其余节点共享。

条件:结构是树形,且修改只走一条(或 \(O(\log)\) 条)路径。

线段树:单点修改天然 \(O(\log n)\)。区间修改需要标记永久化(I.3),否则下传会复制两个儿子。

Treap:split / merge 中经过的节点全部复制。随机优先级存在节点里,复制时一起复制。注意:同一个节点在两个版本中共享时,不能在它上面做原地 pushdown,必须先复制再 pushdown。

平衡树的可持久化必须用 Treap(FHQ)而不是 Splay,原因同 IV.1:Splay 的旋转改变的节点数无界。

并查集:可持久化数组存 \(\text{fa}\),按秩合并不压缩,\(O(\log^2 n)\)。

IV.3 版本差分:信息是可减群时

主席树求区间第 \(k\) 小的本质:版本 \(r\) 的树减版本 \(l-1\) 的树。这要求节点信息是群(有逆元),计数是整数加法群,可以。

推广:任何"前缀可持久化 + 信息可减"的问题都可以用版本差分做区间查询:区间不同数(用"最后出现位置"编码)、区间异或第 \(k\) 大(可持久化 Trie)、区间 \(\text{mex}\)(值域上维护"最后出现位置",查最小的"最后位置 \(< l\)"的值)。

树上版本差分:以 DFS 顺序建版本(每个点的版本 = 父亲版本 + 该点),路径 \(u \to v\) 的信息 \(= T_u + T_v - T_{\text{lca}} - T_{\text{fa}(\text{lca})}\)。例题:洛谷 P2633 Count on a tree。

IV.4 可持久化线段树合并

线段树合并时,被合并的两棵树在新版本中不再需要,可以原地合并;但若需要保留旧版本,就要在合并时新建节点而不是复用 \(a\)。

复杂度:递归次数分析(II.5)不变,所以新建节点数 \(O(n\log n)\)。空间通常开到 \(n\log n \times\) 常数。

例题:洛谷 P3302 [SDOI2013] 森林(可持久化线段树 + 启发式合并 + 树上主席树)。

IV.5 时间倒流

把操作序列反过来做:删除变插入,"何时第一次断开"变"何时最后一次连上"。配合并查集能解决很多"离线删除"问题(如 CF 1213G、洛谷 P1197 [JSOI2008] 星球大战)。

本质:并查集只支持加边不支持删边,而时间倒流把删边序列变成了加边序列。这是"利用离线消除不可逆操作"的最简单例子,与线段树分治(III.3)目的相同,但只适用于"操作序列可完全逆转"的情形。

IV.6 本节题单

题 考什么
洛谷 P3919 / P3834 可持久化线段树 1 / 2 基础
洛谷 P3402 可持久化并查集 可持久化数组
洛谷 P5055 可持久化文艺平衡树 可持久化 Treap 带标记
洛谷 P4735 最大异或和 可持久化 Trie
洛谷 P2633 Count on a tree 树上版本差分
洛谷 P4137 Rmq Problem / mex 值域上"最后出现位置"
洛谷 P3302 [SDOI2013] 森林 可持久化 + 启发式合并
洛谷 P1197 星球大战 时间倒流

V. 阈值与平衡

V.1 平方根的来源

两种代价 \(A(B)\) 和 \(C(n/B)\),当乘积恒定时,\(B = \sqrt{n}\) 最小化和。更一般地,若操作 1 有 \(q_1\) 次代价 \(O(B)\),操作 2 有 \(q_2\) 次代价 \(O(n/B)\),最优 \(B = \sqrt{nq_2/q_1}\)。

根号算法的本质:把问题按某个量 \(x\) 分成"\(x \le B\)"和"\(x > B\)",前者预处理 / 打表,后者暴力,两边复杂度相等时最优。

V.2 序列分块与值域分块的组合

序列分块:整块打标记、散块暴力。
值域分块:值域 \([1, V]\) 分 \(\sqrt V\) 块,插入删除 \(O(1)\),查询前缀 / 第 \(k\) 小 \(O(\sqrt V)\)。

为什么值域分块比树状数组更适合莫队:莫队移动指针 \(O(n\sqrt q)\) 次(多),查询 \(q\) 次(少)。值域分块让"多的操作" \(O(1)\)、"少的操作" \(O(\sqrt V)\),总 \(O(n\sqrt q + q\sqrt V)\);树状数组是两边都 \(\log\),总 \(O(n\sqrt q\log n)\)。"多的操作便宜,少的操作贵"是根号平衡的通用原则。

V.3 分块预处理:区间众数

问题(洛谷 P4168 蒲公英):静态区间众数。

  • 预处理 \(f[i][j]\) = 第 \(i\) 到第 \(j\) 块的众数,\(O(n\sqrt n)\)。
  • 预处理每个值的出现位置列表。
  • 查询:整块部分的众数是候选之一;散块中每个值也是候选,对每个候选用出现位置列表二分求区间内出现次数,\(O(\sqrt n\log n)\)。

优化到 \(O(\sqrt n)\):预处理 \(\text{cnt}[i][v]\) = 前 \(i\) 块中 \(v\) 的出现次数(空间 \(O(n\sqrt n)\),可用 short 或滚动),散块暴力时直接查。

回滚莫队做同一问题 \(O(n\sqrt n)\),但空间 \(O(n)\)。

V.4 莫队二次离线

问题(洛谷 P4887):区间内满足 \(\text{popcount}(a_i \oplus a_j) = k\) 的点对数。

普通莫队每次移动指针 \(x\) 需要求"\([l, r]\) 中有多少个 \(a_j\) 与 \(a_x\) 异或后 popcount 为 \(k\)",这需要 \(O(\binom{14}{k})\),总 \(O(n\sqrt n\cdot\binom{14}{k})\) 太慢。

二次离线:把每次指针移动的贡献 \(f(x, [l, r])\) 拆成前缀差 \(f(x, [1, r]) - f(x, [1, l-1])\)。指针从 \(r\) 移到 \(r'\) 时需要 \(\sum_{x=r+1}^{r'} f(x, [1, x-1])\) 这一类(只依赖 \(x\),\(O(n)\) 预处理)减去 \(\sum_{x=r+1}^{r'} f(x, [1, l-1])\)(依赖 \((x, l-1)\),把 \((x\) 的区间 \([r+1, r'],\ l-1)\) 作为一个"二次询问"存下来)。

二次询问只有 \(O(q)\) 个,但每个涉及 \(O(\sqrt n)\) 长度的 \(x\) 区间。按 \(l-1\) 排序后扫描,扫到位置 \(p\) 时把 \(a_p\) 加入一个 \(O(\binom{14}{k})\) 更新、\(O(1)\) 查询的结构,然后回答所有"右端为 \(p\)"的二次询问,每个二次询问 \(O(\sqrt n)\)。

总 \(O(n\binom{14}{k} + n\sqrt q)\)。

本质:莫队把"一次询问"变成 \(O(\sqrt n)\) 次"指针移动",二次离线再把"指针移动的贡献"变成"前缀贡献之差",后者可以按另一个维度离线。这是"离线套离线"。 同样的框架解决区间逆序对 \(O(n\sqrt n)\)(洛谷 P5047)。

V.5 根号分治的常见形态

分治量 小的一边 大的一边 例
模数 \(x\) 预处理 \(f[x][y]\) 暴力跳 \(n/x\) 步 哈希冲突 P3396
度数 暴力枚举邻居 大点少,每个单独维护 三元环 P1989、CF 1039D
出现次数 暴力扫描所有位置 值少,两两配对预处理 出现次数 \(\ge \sqrt n\) 的值不超过 \(\sqrt n\) 个
跳跃步长 预处理每个起点的答案 暴力跳 弹飞绵羊的根号做法
询问区间长度 短区间暴力 长区间用重结构

V.6 分块 + bitset

问题:区间"是否存在两个数之和 / 之差 / 之积 为 \(v\)"。

把区间内的值集合压成 bitset,\(a + b = v\) 转化为 \(\text{bs} \,\&\, (\text{rev}(\text{bs}) \gg (V - v))\) 非空。莫队维护 bitset,\(O(n\sqrt q + qV/64)\)。例题:洛谷 P3674 小清新人渣的本愿。

分块 + bitset 判集合相等:每块预处理 bitset,询问时整块 or 起来,散块单独加,\(O((n/B + B)V/64)\)。

V.7 KD-tree 的复杂度

结构:交替按 \(x\)、\(y\) 中位数分割的二叉树。

矩形查询 \(O(\sqrt n)\) 的证明:矩形的一条边(一条直线)与 KD-tree 的区域相交时,每两层(一次 \(x\) 分割、一次 \(y\) 分割)把区域分成 4 块,一条直线最多穿过其中 2 块。所以 \(T(n) = 2T(n/4) + O(1) = O(\sqrt n)\)。四条边总共 \(O(\sqrt n)\)。

\(k\) 维:\(O(n^{1 - 1/k})\)。三维已经是 \(O(n^{2/3})\),很少用。

插入:用替罪羊式重构(子树不平衡到 \(\alpha\) 比例时整体重建),均摊 \(O(\log^2 n)\)。

KD-tree 做最近点对是启发式,最坏 \(O(n)\),不要在对抗性数据上依赖它。

KD-tree 的定位:二维数点带修改、或"矩形加 + 单点查询"时,是树套树之外的另一选择,代码短,复杂度 \(O(\sqrt n)\) 而不是 \(\log^2\)。

V.8 本节题单

题 考什么
洛谷 P4168 蒲公英 分块众数
洛谷 P5906 回滚莫队 回滚
洛谷 P4887 莫队二次离线 二次离线
洛谷 P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II 二次离线逆序对
洛谷 P3396 哈希冲突 根号分治
洛谷 P1989 无向图三元环计数 度数分治
洛谷 P3674 小清新人渣的本愿 莫队 + bitset
洛谷 P4148 简单题 KD-tree 带插入
洛谷 P4390 [BalkanOI2007] Mokia KD-tree / CDQ
洛谷 P3203 [HNOI2010] 弹飞绵羊 分块 / LCT
洛谷 P5356 [Ynoi2017] 由乃打扑克 分块 + 块内排序

VI. 树到序列的映射

VI.1 四种序

序 性质 用途
DFS 序(每点一次) 子树 = 连续区间 子树修改 / 查询
欧拉序(每点进出各一次,\(2n\)) 路径 \(u\to v\) = 区间内出现奇数次的点 树上莫队
欧拉序(每次访问都记,\(2n-1\)) LCA = 区间深度最小的点 ST 表 \(O(1)\) LCA
重链剖分序 子树连续 且 重链连续 路径 = \(O(\log n)\) 段

重链剖分序同时满足子树连续和链连续,这是它优于其他序的原因,也是为什么树剖能同时做子树操作和路径操作。

VI.2 三种剖分的本质

重链剖分:按子树大小选重儿子。每走一条轻边子树大小至少减半 \(\Rightarrow\) 根到任意点的轻边数 \(\le \log n\)。优化目标:路径经过的链数。

长链剖分:按子树深度选重儿子。所有长链长度之和 \(= n\);从任意点跳到所在长链顶,再跳一次到父亲,链顶深度至少翻倍(长链性质)\(\Rightarrow\) 跳 \(O(\sqrt n)\) 次;但\(k\) 级祖先 \(O(1)\)(先倍增跳 \(2^{\lfloor\log k\rfloor}\),落到的链长 \(\ge 2^{\lfloor\log k\rfloor} \ge\) 剩余步数)。优化目标:与深度相关的合并。

实链剖分(LCT):实边可以任意变化,每条实链用 Splay 维护,均摊 \(O(\log n)\)。优化目标:动态。

全局平衡二叉树:静态版 LCT。每条重链按子树大小带权建平衡二叉树,深度严格 \(O(\log n)\)。优化目标:去掉 Splay 的均摊和 LCT 的常数,用于动态 DP。

VI.3 长链剖分优化 DP 的实现细节

\(f[u][d]\) = 子树 \(u\) 中深度为 \(d\)(相对 \(u\))的信息。

  • 长儿子 \(h\) 的数组 \(f[h][\cdot]\) 通过指针偏移变成 \(f[u][\cdot + 1]\),\(O(1)\)。
  • 短儿子 \(v\) 的数组暴力合并进 \(f[u]\),代价 \(O(\text{len}(v))\)。
  • 每条长链只会作为"短儿子"被合并一次(在链顶的父亲处),总 \(O(\sum \text{len}) = O(n)\)。

内存分配:对每条长链分配一段长为链长的连续内存,链上每个点指向这段内存的不同偏移。

例题:CF 1009F Dominant Indices(子树中数量最多的深度)、洛谷 P5904 Hotels 加强版(三个点两两距离相等的三元组数,\(f[u][d]\) 和 \(g[u][d]\) 两个数组)。

VI.4 点分树的信息组织

点分树深度 \(O(\log n)\)。任意两点 \(u, v\) 的原树路径必经过它们在点分树上的 LCA \(c\),且 \(\text{dist}(u,v) = \text{dist}(u,c) + \text{dist}(c,v)\)。

每个点 \(c\) 维护两个结构:

  • \(A_c\):\(c\) 的分治块内所有点,按 \(\text{dist}(x, c)\) 为下标的信息(如树状数组 / 动态开点线段树上的点权和)。
  • \(B_c\):同样的点,但按 \(\text{dist}(x, \text{fa}(c))\) 为下标(用于在父亲处去重)。

查询"到 \(u\) 距离 \(\le k\) 的点权和":\(u\) 在点分树上的所有祖先 \(c\)(含自己),累加 \(A_c[\le k - \text{dist}(u, c)]\),减去 \(B_{c'}[\le k - \text{dist}(u, c)]\),其中 \(c'\) 是 \(c\) 的儿子且是 \(u\) 的祖先(这部分在 \(A_{c'}\) 已经算过)。\(O(\log^2 n)\)。

修改 \(u\) 的点权:对 \(u\) 的每个祖先 \(c\) 更新 \(A_c\) 和 \(B_c\)。\(O(\log^2 n)\)。

空间:每个点在 \(O(\log n)\) 个祖先的结构里出现,用动态开点 \(O(n\log^2 n)\),或对每个 \(c\) 开长度为块直径的数组,总 \(O(n\log n)\)。

\(\text{dist}(u, c)\) 的计算:预处理时对每个 \(c\) 存下块内所有点到它的距离(DFS 一次),空间同上。不要在线用 LCA 算,常数大。

VI.5 LCT 维护子树信息

LCT 的 Splay 只维护实链,虚子树的信息必须单独累加。每个点存 \(\text{vir}[x]\) = 所有虚儿子的子树信息之和,\(\text{sum}[x] = \text{sum}[\text{左}] + \text{sum}[\text{右}] + \text{vir}[x] + \text{val}[x]\)。

\(\text{vir}\) 在何时变化:只有 access 中"换右儿子"的那一步(旧右儿子变虚,新右儿子变实)和 link 时(新儿子变虚)。要求信息可减(群),否则要用可删除的结构(如平衡树、multiset)维护 \(\text{vir}\)。

例题:洛谷 P4219 大融合(子树大小)、洛谷 P3703 [SDOI2017] 树点涂色(LCT 的 access 操作本身就是"路径染新色",用线段树维护 DFS 序上的颜色数变化)。

VI.6 LCT 维护 MST 与"边化点"

要在 LCT 上维护边权信息,把每条边拆成一个点(权值为边权),连接两个端点。\(\text{cut}\) 一条边就是 cut 两次。

动态 MST:按边权排序依次加边,若两端已连通,查询路径上最大边权点,若大于当前边则 cut 掉那条边、link 新边。

最小差值生成树(洛谷 P4234):边按权排序,滑动窗口。加入新边时,若成环则删环上最小边(用 LCT 找);维护当前生成树的最小边(multiset),窗口右移时更新答案。

VI.7 虚树

只保留 \(k\) 个关键点及其两两 LCA(共 \(\le 2k - 1\) 个点),按 DFS 序排序后用栈 \(O(k\log n)\) 建树。虚树上的边权是原树路径的信息(距离、最小边权等,用倍增或树剖预处理)。

适用:多次询问,每次给一个点集,做一遍 \(O(\text{点集大小})\) 的树 DP。总 \(O(\sum k\log n)\)。例题:洛谷 P2495 [SDOI2011] 消耗战。

VI.8 树上启发式合并 vs 线段树合并 vs 长链剖分 vs 点分治

问题特征 用什么
子树内统计,离线,信息可加删 DSU on tree
子树内维护值域结构,在线 线段树合并
子树内按深度统计 长链剖分
路径统计(两点之间) 点分治
路径统计 + 修改 点分树
路径修改 / 查询,信息是半群 树剖 + 线段树
动态加边删边 LCT
树上 DP 带单点修改 动态 DP(树剖 / 全局平衡)

VI.9 本节题单

题 考什么
洛谷 P5903 树上 k 级祖先 长链剖分
CF 1009F Dominant Indices 长链剖分 DP
洛谷 P5904 Hotels 加强版 长链剖分双数组
洛谷 P6329 点分树 点分树模板
洛谷 P3241 [HNOI2014] 开店 点分树 + 可持久化
洛谷 P4219 大融合 LCT 子树信息
洛谷 P3703 树点涂色 LCT access 的本质
洛谷 P4234 最小差值生成树 LCT 维护 MST
洛谷 P2495 消耗战 虚树
洛谷 P4751 动态 DP 加强版 全局平衡二叉树
SP10707 COT2 树上莫队

VII. 数据结构优化 DP 的全景

DP 优化的本质是"转移方程 \(f_i = \text{opt}_j (f_j + w(j, i))\) 中,把对 \(j\) 的枚举换成数据结构查询"。按 \(w(j, i)\) 的形态分类:

\(w(j,i)\) 的形态 结构 复杂度
只依赖 \(j\),且 \(j\) 的范围是区间 线段树 / 单调队列 \(\log\) / \(1\)
\(k_j \cdot x_i + b_j\)(一次函数) 李超树(任意顺序)/ 斜率优化(单调) \(\log\) / \(1\)
凸函数、决策单调 分治 / 二分队列 \(\log\)
依赖 \(j\) 的多个属性满足偏序 CDQ \(\log^2\)
\(f\) 的转移是线性变换 矩阵 + 线段树(动态 DP) \(\log^2\)
转移来自区间的每个点("区间到点") 线段树优化建图 建图 \(n\log n\) 条边
\(w\) 是区间信息且随 \(i\) 单调变化 单调栈 + 线段树("历史"技巧) \(\log\)

VII.1 线段树优化建图

从点 \(u\) 向区间 \([l, r]\) 中的每个点连边:建一棵"出树"(父连子,边权 0),\(u\) 向覆盖 \([l, r]\) 的 \(O(\log n)\) 个节点连边。从区间向点连边用"入树"(子连父)。区间向区间连边用一个虚点中转。总边数 \(O(n\log n)\)。

用途:最短路、拓扑、2-SAT 中的"区间约束"。例题:CF 786B Legacy、洛谷 P6348 [PA2011] Journeys。

VII.2 决策单调性 + 数据结构

当 \(w\) 满足四边形不等式但不能 \(O(1)\) 计算(例如 \(w(j,i)\) 是区间 \([j, i]\) 内的不同数个数),用分治求决策点,并像莫队一样移动指针维护 \(w\)。每层分治指针移动 \(O(n)\),总 \(O(n\log n\cdot\text{单次移动代价})\)。例题:CF 868F Yet Another Minimization Problem。

VII.3 平衡树维护凸包 / 斜率优化的在线版本

斜率优化要求 \(x_j\) 单调插入、查询斜率单调,否则用:

  • 李超树:插入任意直线,查任意 \(x\),\(O(\log n)\)。适合"最值"。
  • 平衡树 / set 维护凸包:插入任意点,动态维护上凸壳,查询时在凸壳上二分。适合需要"凸壳本身"的场景。
  • CDQ 分治:离线,左半建凸壳,右半查询,\(O(n\log^2 n)\)。

VIII. 经典问题的做法谱系

同一个问题的多种做法,对比它们分别用了哪个原理。

区间第 \(k\) 小

做法 原理 带修? 复杂度
主席树 IV 版本差分 否 \(\log n\)
整体二分 III 值域分治 是(离线) \(\log n\log V\)
树状数组套主席树 I + IV 是 \(\log^2 n\)
分块 + 值域分块 V 是 \(\sqrt n\)

区间众数:分块预处理(V.3)/ 回滚莫队(II + V)/ 强制在线用分块 + 出现位置二分。

区间不同数个数:离线 BIT("最后出现位置")/ 主席树在线 / 莫队。

区间逆序对:CDQ(离线,静态)\(n\log^2 n\) / 莫队二次离线 \(n\sqrt n\) / 分块 + 块内排序 \(n\sqrt{n\log n}\)。

区间 mex:主席树(IV.3)/ 回滚莫队 / 分块。

动态逆序对:CDQ(时间作为一维)/ 树套树。

树上路径第 \(k\) 小:树上主席树(IV.3)/ 点分治 + 整体二分 / 树剖 + 树套树。

动态图连通性:线段树分治 + 可撤销并查集(离线)/ LCT(只能处理森林)/ 在线一般图用 Holm–de Lichtenberg–Thorup(竞赛不考)。


IX. 常见错误清单

  1. 把非半群的信息塞进线段树。 中位数、众数不能合并,必须换值域结构或根号。
  2. 历史最值标记直接用 \(\max\) 累加。 必须用 \((a, b)\) 二元组复合,\(b = \max(b_1, a_1 + b_2)\)。
  3. 动态 DP 的矩阵乘顺序反了。 链顶在左,叶在右,\(G_{u_1}\otimes\cdots\otimes G_{u_k}\)。
  4. Beats 没有维护严格次大值就打标记。 \(\text{mx2} = \text{mx1}\) 时必须合并计数。
  5. CDQ 优化 DP 用了"左、右、跨"的顺序。 必须"左、跨、右"。
  6. 线段树分治用了路径压缩并查集。 不可撤销。
  7. 可持久化 Treap 原地 pushdown。 共享节点必须先复制。
  8. 可持久化线段树做区间赋值。 赋值标记不可交换,不能永久化。
  9. 二进制分组期望支持删除。 不支持,要配线段树分治。
  10. 莫队块长写死 \(\sqrt n\)。 最优 \(n/\sqrt q\);带修 \(n^{2/3}\);二次离线中的块长同普通莫队。
  11. 点分树查询忘记减去儿子方向的重复。 \(B_{c'}\) 那一项。
  12. LCT 维护子树信息时 \(\text{vir}\) 更新漏了 link。 access 和 link 两处都要改。
  13. 长链剖分内存分配没按链分配连续段。 指针偏移要求同链连续。
  14. KD-tree 用于最近点对当成严格复杂度。 是启发式。
  15. 颜色段均摊用于没有区间赋值的题。 段数不减,复杂度错。

X. 课堂练习

  1. 证明历史最值标记的复合 \((a_1,b_1)\circ(a_2,b_2) = (a_1+a_2, \max(b_1, a_1+b_2))\) 满足结合律。
  2. 写出"历史和"(每个位置的值在每个时刻的累加)的标记及其复合。(提示:标记 \((a, s, t)\):加量、历史和增量、时间长度。)
  3. 把树上最小点覆盖写成动态 DP 的 \((\min, +)\) 矩阵。
  4. 证明 Beats 只有 chmin 时势能 \(\Phi = \sum_{\text{节点}}(\text{不同值个数})\) 初始为 \(O(n\log n)\),且每次穿透至少减 1。
  5. 给出一个信息是半群但不是交换半群的例子,并说明为什么它不能标记永久化。
  6. 用 CDQ 套 CDQ 的框架写出四维偏序的伪代码,标出哪里需要"来源标记"。
  7. 说明为什么线段树分治可以处理"删除"而二进制分组不能。
  8. 推导莫队二次离线中"\(\sum_{x=r+1}^{r'} f(x, [1, x-1])\)"为什么可以 \(O(n)\) 预处理。
  9. 证明 KD-tree 矩形查询 \(T(n) = 2T(n/4) + O(1)\)。
  10. 对点分树,写出"修改点权"时需要更新的所有位置。
  11. 长链剖分 \(k\) 级祖先:为什么先跳 \(2^{\lfloor\log k\rfloor}\) 后落到的链长一定 \(\ge\) 剩余步数?
  12. 给出一道题,它同时用到原理 III(分治)和原理 IV(可持久化),说明各自解决什么。(答:火星商店问题。)

XI. 综合题单

难度 题目 原理
省选 洛谷 P4314 CPU 监控 I
省选 洛谷 P6242 线段树 3 I + II
省选 洛谷 P4719 动态 DP I + VI
省选 洛谷 P5024 保卫王国 I + VI
省选 洛谷 P3521 ROT-Tree Rotations II
省选 洛谷 P3332 K 大数查询 III
省选 洛谷 P4585 火星商店问题 III + IV
省选 洛谷 P3302 森林 IV + II
省选 洛谷 P4887 莫队二次离线 V
省选 洛谷 P3241 开店 VI + IV
省选 洛谷 P4219 大融合 VI
省选 CF 786B Legacy VII
NOI 洛谷 P4751 动态 DP 加强版 I + VI 全局平衡
NOI 洛谷 P5047 Yuno loves sqrt technology II V 二次离线
NOI 洛谷 P5693 Kinetic Tournament I + II KTT
NOI 洛谷 P3703 树点涂色 VI LCT access 本质
NOI 洛谷 P4292 重建计划 III 点分治 + 二分
NOI 洛谷 P5356 由乃打扑克 V 分块极限
NOI 洛谷 P4117 [Ynoi2018] 五彩斑斓的世界 II + V 势能分块
NOI 洛谷 P4690 [Ynoi2016] 镜中的昆虫 II 颜色段均摊 + 三维偏序

附录:一页纸总结

I. 半群与作用:信息能写出"两段合并"就能上线段树;标记能写出"两次复合"就能延迟;复合可交换才能永久化;转移是线性的就用矩阵;无修改的半群用猫树 \(O(1)\)。

II. 势能:找一个只减不增的量,让每次深入递归都消耗它。Beats 用不同值个数,开方用 \(\log\log\),取模用 \(\log\),颜色段用段数,合并用节点数,DSU on tree 用轻边数。

III. 分治:下标(CDQ)、时间(线段树分治)、值域(整体二分)三选一离线;在线化用二进制分组;树上用点分治。

IV. 可逆:按秩不压缩可撤销;树形结构路径复制可持久化;信息是群才能版本差分;操作可逆转时时间倒流。

V. 平衡:\(B = \sqrt{nq_2/q_1}\);多的操作便宜少的操作贵;离线套离线(二次离线);KD-tree 是 \(O(\sqrt n)\) 不是 \(\log\)。

VI. 树:重链管路径段数,长链管深度合并,实链管动态,全局平衡管严格 \(\log\);点分树把路径问题变成 \(O(\log)\) 个祖先的查询。

看题顺序:合并?→ 势能?→ 离线?→ 历史?→ 平衡?→ 线性化?

posted @ 2026-09-20 08:32  ylzqwq  阅读(43)  评论(0)    收藏  举报