LCT
LCT (Link - Cut Tree)
今天,YCC 本来是想搞一搞数论,但是老吕说:今天我闲着,给你们讲一讲LCT。
LCT 是什么?
是一个由若干棵子结点无序的有根树组成的森林,支持对树的分割, 合并, 对某个点到它的根的路径的某些操作, 以及对某个点的子树进行的某些操作。
基本概念
原树:就是原来的树,实孩子,实边,实链,都是原树中的。
辅助树:是很多splay 构成的树,程序中真正操作的都是辅助树。
实孩子:这个实孩子与轻重链剖分中的重孩子还不同。
轻重链剖分中的重孩子是根据子树的大小来确定的,但是LCT中的实孩子是不确定的,当我们需要它时,就将它从虚孩子变为实孩子。
实边:连接实孩子的边称为实边。
实链:实边组成的的链为实链。
基本思想
思想和树链剖分的思想类似,每个点(叶子点除外)中最多有一个孩子为实孩子(可能没有)。连接实孩子的边称为实边,实边组成的的链为实链。
把实链交给splay维护。按深度作为关键字,每一个 splay 的中序遍历就是在原树中从上往下的一条链。
这样还剩下一些虚孩子,和各个分散的 splay。 让每棵 splay 的根节点的父亲节点指向原树中这条链 的父亲节点(即实链链顶的父亲节点)。
这类连边与通常 Splay 中(也就是原树上的实边)的连边有所不同,区别在于这儿子认父亲,而父亲不认儿子,对应着原树的一条 虚边。
基础操作
首先是 splay 和 rotate。
void splay(int p)
{
Push_down(p);
for(; !isroot(p); rotate(p))
if(!isroot(fa(p))) rotate(get(fa(p)) == get(p) ? fa(p) : p);
}
void rotate(int p)
{
int fath = fa(p), x = get(p);
if(!isroot(fa(p))) t[fa(fa(p))].son[get(fa(p))] = p;
fa(p) = fa(fa(p));
t[fath].son[x] = t[p].son[x ^ 1];
fa(t[p].son[x ^ 1]) = fath;
t[p].son[x ^ 1] = fath;
fa(fath) = p;
push_up(fath), push_up(p);
}
isroot(x)
:判断x是否是辅助树的根。
方法:splay 的根结点的父亲并不认这个孩子。原树的根所在的 splay 的父亲点是 \(0\)。
bool isroot(int x)//判断是否为splay中的根
{
return ls(fa(x)) != x && rs(fa(x)) != x;//根节点的父亲不认它这个孩子
}
access(x)
:把 \(x\) 点下面的实边断开,并把 \(x\) 点一路向上边到树的根,(也就是,打通根节点到指定节点的实链,使得一条中序遍历以根开始、以指定点结束的 splay出现。)
方法:把 \(x\) 点伸展到 splay 的根,再把它的右子树连到 y,也就是连上了下一层的实链,然后 y 更新为x,而 x 更新为 x 的父亲,继续向上连接。由于 y 的初始值为0,所以第一次连接也就是断开了下面的实边。
for(int x = 0; p; x = p, p = fa(p))
splay(p), rs(p) = x, push_up(p);
link(x,y)
:连接两个点。
方法:把 \(x\) 点变成所在原树的根,然后把 \(x\) 点的父亲变成 \(y\) 。
void link(int x, int y)
{
makeroot(x);
if(find(y) != x) fa(x) = y;
}
cut(x,y)
:断开两个点间的边。
void cut(int x, int y)
{
makeroot(x);
if(find(y) != x || fa(y) != x || ls(y)) return ;
fa(y) = rs(x) = 0;
push_up(x);
}
makeroot(x)
:把x点变为树的根。
方法:首先的把 \(x\) 点 access 到根,把 \(x\) 点到根变成一个 splay,然后把 \(x\) 伸展到根。由于 \(x\) 点是辅助树在原树中最下面的点,所以这时其它的点都在 \(x\) 的左子树上,只要把左子树变成右子树,\(x\) 也就变成了根。
void makeroot(int p)
{
access(p); splay(p); f(p);
}
find(x)
:查找 \(x\) 所在树的根。
方法:首先把 \(x\) 点 access 到原树的根,并把它 splay 到辅助树的根,这时原树的根就是 \(x\) 左子树中最左侧的点。
int find(int p)
{
access(p); splay(p);
while(ls(p)) push_down(p), p = ls(p);
splay(p);
return p;
}
/
/*
Date:2021.9.7
Source:luogu 3690
konwledge:
*/
#include <iostream>
#include <cstdio>
#define orz cout << "AK IOI" << "\n"
#define fa(x) t[x].fa
#define ls(x) t[x].son[0]
#define rs(x) t[x].son[1]
#define get(x) (rs(fa(x)) == x)
using namespace std;
const int maxn = 3e5 + 10;
inline int read()
{
int f = 0, x = 0; char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) x = x * 10 + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
inline void print(int X)
{
if(X < 0) X = ~(X - 1), putchar('-');
if(X > 9) print(X / 10);
putchar(X % 10 + '0');
}
int n, m;
struct node {
int son[2], fa, val, sum, lzy;
} t[maxn];
int kcnt;
bool isroot(int x)
{
return ls(fa(x)) != x && rs(fa(x)) != x;
}
void push_up(int p)
{
t[p].sum = t[ls(p)].sum ^ t[rs(p)].sum ^ t[p].val;
}
void f(int p)
{
swap(ls(p), rs(p));
t[p].lzy ^= 1;
}
void push_down(int p)
{
if(t[p].lzy) f(ls(p)), f(rs(p)), t[p].lzy = 0;
}
void rotate(int p)
{
int fath = fa(p), x = get(p);
if(!isroot(fa(p))) t[fa(fa(p))].son[get(fa(p))] = p;
fa(p) = fa(fa(p));
t[fath].son[x] = t[p].son[x ^ 1];
fa(t[p].son[x ^ 1]) = fath;
t[p].son[x ^ 1] = fath;
fa(fath) = p;
push_up(fath), push_up(p);
}
void Push_down(int p)
{
if(!isroot(p)) Push_down(fa(p));
push_down(p);
}
void splay(int p)
{
Push_down(p);
for(; !isroot(p); rotate(p))
if(!isroot(fa(p))) rotate(get(fa(p)) == get(p) ? fa(p) : p);
}
void access(int p)
{
for(int x = 0; p; x = p, p = fa(p)) splay(p), rs(p) = x, push_up(p);
}
void makeroot(int p)
{
access(p); splay(p); f(p);
}
int find(int p)
{
access(p); splay(p);
while(ls(p)) push_down(p), p = ls(p);
splay(p);
return p;
}
void split(int x, int y)
{
makeroot(x); access(y); splay(y);
}
void link(int x, int y)
{
makeroot(x);
if(find(y) != x) fa(x) = y;
}
void cut(int x, int y)
{
makeroot(x);
if(find(y) != x || fa(y) != x || ls(y)) return ;
fa(y) = rs(x) = 0;
push_up(x);
}
int main()
{
n = read(), m = read();
for(int i = 1; i <= n; i++) t[i].val = read();
for(int i = 1; i <= m; i++)
{
int opt = read(), x = read(), y = read();
if(opt == 0) split(x, y), print(t[y].sum), puts("");
if(opt == 1) link(x, y);
if(opt == 2) cut(x, y);
if(opt == 3) splay(x), t[x].val = y;
}
return 0;
}