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;
}