【模板】Link-Cut-Tree

模板题
LCT的讲解略过···参考别人的博客

简单来说就是用 s p l a y splay splay维护一棵树的某条链上的信息
主要操作有:
a c c e s s access access:连通一个点到根的链
m a k e r o o t makeroot makeroot:将 x x x变成根
f i n d r o o t findroot findroot:找到树根
l i n k link link:将两个点连上边
c u t cut cut:删掉两个点之间的边
s p l i t split split:将一条链上的信息提取出来

有一些细节需要注意:
l i n k link link c u t cut cut首先需要一些条件,其次 c u t cut cut的条件要注意 c h [ x ] [ 1 ] = = 0 ch[x][1]==0 ch[x][1]==0
r o t a t e rotate rotate操作的写法,一开始顺序不对就无限递归了 q w q qwq qwq
s p l a y splay splay的时候判断 ! i s r o o t ( f a ) !isroot(fa) !isroot(fa)

代码在这:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ls ch[x][0]
#define rs ch[x][1]
#define fa f[x]
#define LL long long
#define N 300005
using namespace std;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

namespace lct{
	int f[N],ch[N][2],rev[N],sum[N],w[N];
	inline int get(int x){return ch[fa][1]==x;}
	inline int isrt(int x){return ch[fa][0]!=x && ch[fa][1]!=x;}
	inline void update(int x){sum[x]=sum[ls]^sum[rs]^w[x];}
	inline void rever(int x){rev[x]^=1;swap(ls,rs);}
	inline void pushdown(int x){
		if(rev[x]){
			if(ls) rever(ls);
			if(rs) rever(rs);
			rev[x]=0;
		}
	}
	void pushup(int x){if(!isrt(x)) pushup(fa);pushdown(x);}
	inline void rotate(int x){
		int old=f[x],oldf=f[old],wh=get(x);
		if(!isrt(old)) ch[oldf][get(old)]=x;//要写在前面 
		f[ch[x][wh^1]]=old; ch[old][wh]=ch[x][wh^1];
		f[old]=x; ch[x][wh^1]=old; f[x]=oldf;
		update(old); update(x);
	}
	inline void splay(int x){
		pushup(x);
		for(;!isrt(x);rotate(x))
			if(!isrt(fa)) rotate(get(x)==get(fa)?fa:x);//
	}
	
	inline void access(int x){
		for(int y=0;x;y=x,x=fa) splay(x),rs=y,update(x);
	}
	inline void makeroot(int x){
		access(x); splay(x); rever(x);
	}
	inline int findroot(int x){
		access(x); splay(x);
		while(ls) pushdown(x),x=ls; return x;
	}
	inline void link(int x,int y){
		makeroot(x); f[x]=y;
	}
	inline void cut(int x,int y){
		makeroot(x); access(y); splay(y);
		if(ch[y][0]==x && ch[x][1]==0)//注意ch[x][1]==0 
			f[x]=ch[y][0]=0,update(y);
	}
	inline void split(int x,int y){
		makeroot(x); access(y); splay(y);
	}
};

int n,m,opt,x,y;

int main(){
	n=rd(); m=rd();
	for(int i=1;i<=n;i++) lct::w[i]=rd();
	while(m--){
		opt=rd(); x=rd(); y=rd();
		if(opt==0) lct::split(x,y),printf("%d\n",lct::sum[y]);
		if(opt==1)
			if(lct::findroot(x)!=lct::findroot(y)) 
				lct::link(x,y);
		if(opt==2) 
			if(lct::findroot(x)==lct::findroot(y)) 
				lct::cut(x,y);
		if(opt==3) lct::w[x]=y,lct::splay(x);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值