前置知识:Splay,树链剖分
L C T \huge{LCT} LCT
简介
LCT(Link/Cut-Tree),又名实链剖分
用于解决动态树问题
引入
给定一棵树,要求以下操作:
1 x y
代表询问从 x x x 到 y y y 的路径上的点的权值的和。2 x y
代表连接 x x x 到 y y y,若 x x x 到 y y y 已经联通则无需连接。3 x y
代表删除边 ( x , y ) (x,y) (x,y),不保证边 ( x , y ) (x,y) (x,y) 存在。4 x y
代表将点 x x x 上的权值变成 y y y。
像这类问题,存在加边和删边操作,被称为动态树问题
概念
- 实边:为平衡时间复杂度而定义,无实际意义,每个节点连0,1,2条实边连向儿子
- 实链:由多条实边组成,构成一个 S p l a y Splay Splay结构
- 虚边:连接两个实链
- 辅助树是可以在满足中序遍历、Splay 的性质下任意换根的。
- 虚实链变换可以轻松在辅助树上完成,这也就是实现了动态维护树链剖分。
辅助树
辅助树是由多个 S p l a y Splay Splay构成的,多个辅助树结构就构成了森林
- 辅助树由多棵 Splay 组成,每棵 Splay 维护原树中的一条路径,且中序遍历这棵 Splay 得到的点序列,从前到后对应原树“从上到下”的一条路径。
- 原树每个节点与辅助树的 Splay 节点一一对应。
- 辅助树的各棵 Splay 之间并不是独立的。每棵 Splay 的根节点的父亲节点本应是空,但在 LCT 中每棵 Splay 的根节点的父亲节点指向原树中 这条链 的父亲节点(即链最顶端的点的父亲节点)。这类父亲链接与通常 Splay 的父亲链接区别在于儿子认父亲,而父亲不认儿子,对应原树的一条 虚边。因此,每个连通块恰好有一个点的父亲节点为空。
- 由于辅助树的以上性质,我们维护任何操作都不需要维护原树,辅助树可以在任何情况下拿出一个唯一的原树,我们只需要维护辅助树即可。
如图,现在我们有一棵原树
它的辅助树如下
函数声明
- Splay系列函数
pushrev(x)
反转函数,用于懒标记PushUp(x)
更新PushDown(x)
下传标记rotate(x)
旋转一次splay(x)
S p l a y Splay Splay核心操作
- LCT操作
Access(x)
将 x x x与 根节点 的路径上所有边改为实边IsRoot(x)
判断 x x x是否是所在Splay的根。makeroot(x)
将 x x x变为原树的根。findroot(x)
查找原树根split(x, y)
使 x 与 y 在同一个 Splay 中link(x, y)
连接 x 与 ycut(x, y)
切断 x 与 y 之间的边
Splay
不多说,但有一点不一样,已在代码中标注
void pushrev(int x) // 翻转一次x
{
swap(tr[x].s[0], tr[x].s[1]);
tr[x].rev ^= 1;
}
void pushup(int x)
{
tr[x].sum = tr[tr[x].s[0]].sum ^ tr[x].v ^ tr[tr[x].s[1]].sum; // 这里以xor为例
}
void pushdown(int x)
{
if (tr[x].rev)