LCT


今天,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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM