【Link Cut Tree 模板】洛谷P3690 动态树 LCT 模板

题目戳这里

本模板是从网上复制来的。

操作如下:
access(x,y),将x到y的路径连上实边,其他儿子都变成虚边。
link(x,y),将x和y连起来。
cut(x,y),将x和y断开。

对于修改操作,若将点x的权值修改成y,只需splay(x)然后v[x] = y即可。

findroot(x)==findroot(y)表示连通。

makeroot(x)表示将x选为跟。

split(x,y),将x到y的路径变成一个splay,且y为splay的跟。

模板在下面:

#include <bits/stdc++.h>
#define sc(n) scanf("%d",&n)
#define pt(n) printf("%d\n",n)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define vi vector<int>
#define vl vector<long long>
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 3e5+7;
int fa[maxn],ch[maxn][2],v[maxn],siz[maxn],st[maxn];
bool rev[maxn];
bool nroot(int x) //判断节点是否为一个splay的跟 
{
	return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void pushup(int x) //询问的是x到y路径的异或和 
{
	siz[x] = siz[ch[x][0]] ^ siz[ch[x][1]] ^ v[x]; 
}
void pushr(int x) //翻转标记 
{
	swap(ch[x][0],ch[x][1]);
	rev[x] ^= 1;
}
void pushdown(int x)
{
	if(rev[x])
	{
		if(ch[x][0]) pushr(ch[x][0]);
		if(ch[x][1]) pushr(ch[x][1]);
		rev[x] = 0;
	}
}
void rotate(int x)
{
	int y = fa[x],z = fa[y],k = ch[y][1]==x,w = ch[x][!k];
	if(nroot(y)) ch[z][ch[z][1]==y]=x;
	ch[x][!k] = y,ch[y][k] = w;
	if(w) fa[w] = y;
	fa[y] = x,fa[x] = z;
	pushup(y);
}
void splay(int x)
{
	int y = x,top = 0;
	st[++top] = y;
	while(nroot(y)) st[++top] = y = fa[y];
	while(top) pushdown(st[top--]);
	while(nroot(x))
	{
		y = fa[x];
		int z = fa[y];
		if(nroot(y)) rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
		rotate(x);
	}
	pushup(x);
}
void access(int x)
{
	for(int y=0;x;x=fa[y=x])
	{
		splay(x);
		ch[x][1] = y;
		pushup(x);
	}
}
void makeroot(int x)
{
	access(x);
	splay(x);
	pushr(x);
}
int findroot(int x)
{
	access(x);
	splay(x);
	while(ch[x][0])
	{
		pushdown(x);
		x = ch[x][0];
	}
	splay(x);
	return x;
}
void split(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
}
void link(int x,int y)
{
	makeroot(x);
	if(findroot(y)!=x) fa[x] = y;
}
void cut(int x,int y)
{
	makeroot(x);
	if(findroot(y)==x && fa[y]==x && !ch[y][0])
	{
		fa[y] = ch[x][1] = 0;
		pushup(x);
	}
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",v+i);
	while(m--)
	{
		int t,x,y;
		scanf("%d%d%d",&t,&x,&y);
		if(t==0)
		{
			split(x,y);
			printf("%d\n",siz[y]);
		}
		if(t==1) link(x,y);
		if(t==2) cut(x,y);
		if(t==3)
		{
			splay(x);
			v[x] = y;
		}
	}	
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值