动态树 Link Cut Tree【模板】【LCT】

>Link

luogu P3690


>Description

给定 n 个点以及每个点的权值,要你处理接下来的 m 个操作。
操作有四种,操作从 0 到 3 编号。点从 1 到 n 编号。

  1. x y 代表询问从 x 到 y 的路径上的点的权值的 xor 和。保证 x 到 y 是联通的。
  2. x y 代表连接 x 到 y,若 x 到 y 已经联通则无需连接。
  3. x y 代表删除边 (x,y),不保证边 (x,y) 存在。
  4. x y 代表将点 x 上的权值变成 y。

n ≤ 1 0 5 , m ≤ 3 ∗ 1 0 5 n\le10^5,m\le3*10^5 n105,m3105


>解题思路

(本来好久之前就要写了,但是一直在改其他题,之前打的也搞不见了,所以一直咕到现在QwQ)

大佬的题解

随便说几句:
在树上建很多个splay,每个splay包含的点在树上为一条链,并且深度互不相同(splay按深度建)
这样我们可以根据splay和树的性质搞很多操作

节点的关系:“认父不认子”,在同一棵树上但不在同一个splay上;“认父认子”,在同一棵树上也在同一个splay上

a c c e s s access access:打通 x x x到树上根节点的路,并且这个splay只包含根节点到 x x x路径上的点
m a k e r o o t makeroot makeroot:把 x x x变成树上的根节点,先 a c c e s s access access它,它变成根节点后显然在splay中它从深度最大变成深度最小的了…深度最小的变成深度最大的了,所以我们把整个splay翻转一下,这里用到翻转的懒标记
s p l i t split split:打通 x x x y y y之间的路径,那就把 x x x变成根,然后打通 y y y到根,再splay一下 y y y(把它变成splay中的根)
c o n n e c t connect connect:在 x x x y y y之间连条边,直接让其中一个点变成树根,然后让它的父亲变成另一个点,不然它原来的父亲就会被刷掉
c u t cut cut:切断 x x x y y y之间的边,先 s p l i t split split它们,然后根据 x x x是树根、 y y y是splay根的性质,把他们之间的关系删掉(“不认子也不认父”)
…然后其他很多操作都可以根据这些性质搞

p u s h a l l pushall pushall:注意因为是边用了翻转的懒标记边splay,所以在splay第x个节点前,我们把 x x x到树根上路径的点的懒标记全部操作完先


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 300010
using namespace std;

struct tree
{
    
    
	int val, sum, lazy, f, son[3];
} t[N];
int n, m;

void update (int x)
{
    
    
	int ls = t[x].son[0], rs = t[x].son[1];
	t[x].sum = t[x].val;
	if (ls) t[x].sum ^= t[ls].sum;
	if (rs) t[x].sum ^= t[rs].sum;
}
int identify (int x)
{
    
    
	if (t[t[x].f].son[0] == x) return 0;
	if (t[t[x].f].son[1] == x) return 1;
	return -1;
}
void pushdown (int x)
{
    
    
	if (!t[x].lazy) return;
	swap (t[x].son[0], t[x].son[1]);
	if (t[x].son[0]) t[t[x].son[0]].lazy ^= 1;
	if (t[x].son[1]) t[t[x].son[1]].lazy ^= 1;
	t[x].lazy = 0;
}
void pushall (int x)
{
    
    
	if (identify (x) != -1) pushall (t[x].f);
	pushdown (x);
}
void rorate (int x)
{
    
    
	int y = t[x].f, z = t[y].f;
	int k = identify (x), kk = identify (y);
	t[y].son[k] = t[x].son[k ^ 1], t[t[x].son[k ^ 1]].f = y;
	t[x].son[k ^ 1] = y, t[y].f = x;
	if (kk != -1) t[z].son[kk] = x;
	t[x].f = z;
	update (y);
	update (x);
}
void splay (int x)
{
    
    
	pushall (x);
	int y, z, k, kk;
	while (identify (x) != -1)
	{
    
    
		y = t[x].f;
		z = t[y].f;
		k = identify (x), kk = identify (y);
		if (kk != -1)
		{
    
    
			if (k == kk) rorate (y);
			else rorate (x);
		}
		rorate (x);
	}
}
void access (int x)
{
    
    
	int u = 0;
	while (x)
	{
    
    
		splay (x);
		t[x].son[1] = u;
		update (x);
		u = x;
		x = t[x].f;
	}
}
void makeroot (int x)
{
    
    
	access (x);
	splay (x);
	t[x].lazy ^= 1;
}
void split (int x, int y)
{
    
    
	makeroot (x);
	access (y);
	splay (y);
}
int asksum (int x, int y)
{
    
    
	split (x, y);
	return t[y].sum;
}
int findroot (int x)
{
    
    
	access (x);
	splay (x);
	while (t[x].son[0]) pushdown (x), x = t[x].son[0];
	splay (x);
	return x;
}
void connect (int x, int y)
{
    
    
	makeroot (x);
	if (x == findroot (y)) return;
	t[x].f = y;
}
void cut (int x, int y)
{
    
    
	if (findroot (x) != findroot (y)) return;
	split (x, y);
	if (t[x].f != y || t[x].son[1]) return;
	t[x].f = t[y].son[0] = 0;
	update (y);
}
void change (int x, int val)
{
    
    
	makeroot (x);
	t[x].val = val;
	update (x);
}

int main()
{
    
    
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= n; i++)
	{
    
    
		scanf ("%d", &t[i].val);
		t[i] = (tree){
    
    t[i].val, t[i].val, 0, 0, {
    
    0, 0, 0}};
	}
	int type, x, y;
	while (m--)
	{
    
    
		scanf ("%d%d%d", &type, &x, &y);
		if (type == 0) printf ("%d\n", asksum (x, y));
		else if (type == 1) connect (x, y);
		else if (type == 2) cut (x, y);
		else change (x, y);
	}
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_43010386/article/details/121113307