捋一下思路
模板题:https://www.luogu.org/problemnew/show/P3690
推荐LCT的教程,个人认为很详细,本文做了部分引用:https://www.luogu.org/blog/flashblog/solution-p3690
前置知识:Splay
LCT是一种动态树,支持连边和断边,还有树上路径的查询
LCT似乎是用Splay维护深度,这点知道了会感觉好理解一些
重要的操作有:
-
access(x),将x到根节点的路径上的边都变成重边。循环处理,只有四步——转到根;换儿子;更新信息;当前操作点切换为轻边所指的父亲,转第一步
-
makeroot(x),将x变为所在Splay的根节点。access操作后x变成Splay内深度最大的点,然后像Splay一样把x转上去
-
findroot(x),找所在树的原根。不停找左儿子,因为左儿子维护的深度比当前节点小。
-
split操作。把一个点转到根节点,原路径就是另一个点到根节点的路径。然后把下面的点转上去更新答案。
-
link(x, y),使x的父亲指向y,连一条轻边。如果在同一棵树则不连边
-
cut(x, y),使x和y断边。使x为根后,y的父亲一定指向x,深度相差一定是1。当access(y),splay(y)以后,x一定是y的左儿子,直接双向断开连接。如果不一定存在该边,先判一下连通性(注意findroot(y)以后x成了根),再看看x,y是否有父子关系,还要看y是否有左儿子(因为也可能y的父亲还是x,那么其它的点就在y的左子树中)。
#include <bits/stdc++.h>
#define root 0
using namespace std;
const int maxn = 300010;
struct LCT { // 其实struct Splay会更好QAQ
int son[2], fa, val, s;
bool tag;
}t[maxn];
int st[maxn];
bool isroot(int x) {
return t[t[x].fa].son[0] == x || t[t[x].fa].son[1] == x;
} // 判断节点是否是一颗Splay的根,如果是的话返回值是false,不是返回值是true(其实改成not_root会更好一些QAQ)
void pushup(int x) {
t[x].s = t[t[x].son[0]].s ^ t[t[x].son[1]].s ^ t[x].val;
}
void reverse(int x) {
swap(t[x].son[0], t[x].son[1]);
t[x].tag ^= 1;
}
void pushdown(int x) {
if(t[x].tag) {
if(t[x].son[0]) reverse(t[x].son[0]);
if(t[x].son[1]) reverse(t[x].son[1]);
t[x].tag = 0;
}
}
void rotate(int x) {
int fa = t[x].fa, grandfa = t[t[x].fa].fa, s = t[fa].son[1] == x, tmp = t[x].son[s ^ 1];
if(isroot(fa)) {
t[grandfa].son[t[grandfa].son[1] == fa] = x;
}
t[x].son[s ^ 1] = fa;
t[fa].son[s] = tmp;
if(tmp) {
t[tmp].fa = fa;
}
t[fa].fa = x;
t[x].fa = grandfa;
pushup(fa);
}
void pushdown_all(int x) {
if(isroot(x)) pushdown_all(t[x].fa);
pushdown(x);
}
void splay(int x) {
int fa, grandfa;
pushdown_all(x);
while(isroot(x)) {
fa = t[x].fa, grandfa = t[fa].fa;
if(isroot(fa)) rotate((t[fa].son[0] == x) ^ (t[grandfa].son[0] == fa) ? x : fa);
rotate(x);
// cout << x << " " << fa << " " << grandfa << endl;
}
pushup(x);
} // 以上是splay部分
下面是LCT部分:
inline void access(int x) {
for(register int y = root; x; y = x, x = t[x].fa) {
// cout << "QAQ" << endl;
splay(x);
t[x].son[1] = y;
pushup(x);
// cout << "QAQ" << endl;
}
} // 将该节点到该节点所在Splay的根节点上的所有路径都变为重边
inline void makeroot(int x) {
access(x); splay(x);
reverse(x);
} // 将一个节点变成所在Splay的根
inline int findroot(int x) {
access(x); splay(x);
while(t[x].son[0]) {
pushdown(x);
x = t[x].son[0];
}
splay(x);
return x;
} // 找到所在Splay的根节点
inline void split(int x, int y) {
makeroot(x);
access(y);
splay(y);
} // 分离出x到y的路径
inline void link(int x, int y) {
makeroot(x);
if(findroot(y) == x) return ;
t[x].fa = y;
} // 连边
inline void cut(int x, int y) {
makeroot(x);
if(findroot(y) != x || t[y].fa != x || t[y].son[0]) return ;
t[y].fa = t[x].son[1] = 0;
pushup(x);
} // 断边
主程序:
int main() {
int n = read(), m = read();
for(register int i = 1; i <= n; i++) {
t[i].val = read();
}
while(m--) {
int opt = read(), x = read(), y = read();
if(!opt) {
split(x, y);
write(t[y].s), putchar(10);
}
if(opt == 1) {
link(x, y);
}
if(opt == 2) {
cut(x, y);
}
if(opt == 3) {
splay(x);
t[x].val = y;
}
}
return 0;
}