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;
}
posted @ 2021-09-08 14:50  _程门立雪  阅读(349)  评论(0编辑  收藏  举报