LCT—模板

Link-Cut Tree可以十分灵活地处理树,它可以轻松地将树节点Cut或者Link。

它的核心操作是Access(x)和Makeroot(x),功能分别是将x与原树的根建立起联系(具体来说是一头一尾);将x变成原树的根。

注意其中有一个叫“原树”,一个是“辅助树”。Splay和Rotate操作都是对辅助树而言的。

模板:P3690 【模板】动态树(Link Cut Tree)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;

class Class_LCT
{
private:
	//const int LCT_SIZE = N;
	int Stack[N + 5];
	struct Tree
	{
		int Val, Sum, Father, Rev, Son[2];
	}node[N + 5];
	void Rever(int x)
	{
		swap(node[x].Son[0], node[x].Son[1]);
		node[x].Rev ^= 1;
	}
	void PushUp(int x)
	{
		node[x].Sum = node[x].Val ^ node[node[x].Son[0]].Sum ^ node[node[x].Son[1]].Sum;
	}
	void PushDown(int x)
	{
		if(node[x].Rev)
		{
			Rever(node[x].Son[0]);
			Rever(node[x].Son[1]);
			node[x].Rev = 0;
		}
	}
	bool Which(int x)
	{
		return node[node[x].Father].Son[1] == x;
	}
	void Connect(int x,int y,int d)
	{
		node[node[x].Father = y].Son[d] =x;
	}
	bool IsRoot(int x)  // 是否是Splay中的根
	{
		return node[node[x].Father].Son[0] ^ x && node[node[x].Father].Son[1] ^ x;
	}
	void MakeRoot(int x)
	{
		Access(x),Splay(x),Rever(x);
	}
	int FindRoot(int x) // 找到x的原树中的根节点
	{
		Access(x), Splay(x); // 先将x与原树的根相连,然后转到原树根的位置,由于中序遍历最左的儿子即使根节点(左儿子层数是第一层的)
		while(node[x].Son[0]) PushDown(x), x = node[x].Son[0];
		Splay(x);
		return x;
	}
	void Split(int x,int y)
	{
		MakeRoot(x),Access(y),Splay(y);
	}

	void Rotate(int x)
	{
		int fa = node[x].Father, pa = node[fa].Father, d = Which(x);
		if(!IsRoot(fa)) node[pa].Son[Which(fa)] = x;
		node[x].Father = pa;
		Connect(node[x].Son[d^1], fa, d);
		Connect(fa, x, d^1);
		PushUp(fa);
		PushUp(x);
	}
	void Splay(int x)
	{
		int fa = x, Top = 0;
		while(Stack[++Top] = fa, !IsRoot(fa)) fa = node[fa].Father;
		while(Top) PushDown(Stack[Top]), --Top; // 由于需要从上往下下放标记,所以要用一个堆先处理下
		while(!IsRoot(x))
		{
			fa = node[x].Father;
			if(!IsRoot(fa))// ?
			{
				Rotate(Which(x) ^ Which(fa) ? x : fa);
			}
			Rotate(x);
		}
	}
	void Access(int x) // 断掉x的右儿子(即x为Splay中最深点),然后将x通往原树根的路径打通
	{
		for(int son = 0; x; x = node[son = x].Father)
		{
			Splay(x), node[x].Son[1] = son, PushUp(x);
		}
	}

public:
	void Init(int len, int *data)
	{
		for(int i = 1; i <= len; i++) node[i].Val = data[i];
	}
	void Link(int x,int y)
	{
		MakeRoot(x);
		if(FindRoot(y) != x) node[x].Father = y;
	}
	void Cut(int x,int y)
	{
		MakeRoot(x);
		if(FindRoot(y) == x && node[y].Father == x && !node[y].Son[0])
		{
			node[y].Father = node[x].Son[1] = 0;
			PushUp(x);
		}
	}
	void Updata(int x,int v)
	{
		Splay(x);
		node[x].Val = v;
	}
	int Query(int x,int y)
	{
		Split(x, y);
		return node[y].Sum;
	}
}LCT;

int a[N];

int main()
{
	int T, n;
	scanf("%d%d", &n, &T);
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
	LCT.Init(n, a);
	while(T--)
	{
		int op, x, y;
		scanf("%d%d%d", &op, &x, &y);
		switch(op)
		{
		case 0:printf("%d\n", LCT.Query(x,y));break;
		case 1:LCT.Link(x, y);break;
		case 2:LCT.Cut(x, y);break;
		case 3:LCT.Updata(x, y);break;
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值