前置知识:Splay,树链剖分
LCT\huge{LCT}LCT
简介
LCT(Link/Cut-Tree),又名实链剖分
用于解决动态树问题
引入
给定一棵树,要求以下操作:
1 x y代表询问从 xxx 到 yyy 的路径上的点的权值的和。2 x y代表连接 xxx 到 yyy,若 xxx 到 yyy 已经联通则无需连接。3 x y代表删除边 (x,y)(x,y)(x,y),不保证边 (x,y)(x,y)(x,y) 存在。4 x y代表将点 xxx 上的权值变成 yyy。
像这类问题,存在加边和删边操作,被称为动态树问题
概念
- 实边:为平衡时间复杂度而定义,无实际意义,每个节点连0,1,2条实边连向儿子
- 实链:由多条实边组成,构成一个SplaySplaySplay结构
- 虚边:连接两个实链
- 辅助树是可以在满足中序遍历、Splay 的性质下任意换根的。
- 虚实链变换可以轻松在辅助树上完成,这也就是实现了动态维护树链剖分。
辅助树
辅助树是由多个SplaySplaySplay构成的,多个辅助树结构就构成了森林
- 辅助树由多棵 Splay 组成,每棵 Splay 维护原树中的一条路径,且中序遍历这棵 Splay 得到的点序列,从前到后对应原树“从上到下”的一条路径。
- 原树每个节点与辅助树的 Splay 节点一一对应。
- 辅助树的各棵 Splay 之间并不是独立的。每棵 Splay 的根节点的父亲节点本应是空,但在 LCT 中每棵 Splay 的根节点的父亲节点指向原树中 这条链 的父亲节点(即链最顶端的点的父亲节点)。这类父亲链接与通常 Splay 的父亲链接区别在于儿子认父亲,而父亲不认儿子,对应原树的一条 虚边。因此,每个连通块恰好有一个点的父亲节点为空。
- 由于辅助树的以上性质,我们维护任何操作都不需要维护原树,辅助树可以在任何情况下拿出一个唯一的原树,我们只需要维护辅助树即可。
如图,现在我们有一棵原树

它的辅助树如下

函数声明
- Splay系列函数
pushrev(x)反转函数,用于懒标记PushUp(x)更新PushDown(x)下传标记rotate(x)旋转一次splay(x)SplaySplaySplay核心操作
- LCT操作
Access(x)将xxx与 根节点 的路径上所有边改为实边IsRoot(x)判断xxx是否是所在Splay的根。makeroot(x)将xxx变为原树的根。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)
{
pushrev(tr[x].s[0]), pushrev(tr[x].s[1]);
tr[x].rev = 0;
}
}
void rotate(int x)
{
int y = tr[x].p, z = tr[y].p;
int k

最低0.47元/天 解锁文章
1331





