【学习笔记】动态树 Link Cut Tree

发布时间 2023-04-15 16:53:45作者: flywatre

算法简介

动态树(Link Cut Tree)简称lct,可以维护动态的联通结构和动态链上信息维护问题,高妙数据结构。

算法流程

talk is cheap,show me the code.
洛谷模板题代码。

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}
const int N=100010;
struct Link_Cut_Tree{
	int sum[N],ans[N],son[N][2],fa[N],p[N];
	bool tag[N];
	inline void pushdown(int u){
		if(!tag[u])return ;
		tag[son[u][0]]^=1,tag[son[u][1]]^=1;
		swap(son[u][0],son[u][1]),tag[u]=0;
		return ;
	}
	inline void update(int u){
		ans[u]=sum[u]^ans[son[u][0]]^ans[son[u][1]];
		return ;
	}
	inline bool isroot(int u){return !(son[fa[u]][0]==u||son[fa[u]][1]==u);}
	inline void rotate(int u){
		int x=fa[u],k=(son[x][1]==u);fa[u]=fa[x];
		if(fa[x]&&!isroot(x))son[fa[x]][son[fa[x]][1]==x]=u;
		son[x][k]=son[u][k^1],fa[son[u][k^1]]=x,update(x);
		son[u][k^1]=x,fa[x]=u,update(u);
		return ;
	}
	inline void splay(int u){
		update(u);
		int top=0;
		for(int i=u;;i=fa[i]){
			p[++top]=i;
			if(isroot(i))break;
		}
		for(int i=top;i>=1;i--)pushdown(p[i]);
		while(!isroot(u)){
			int x=fa[u];
			if(!isroot(x)){
				if((son[fa[x]][0]==x)^(son[x][0]==u))rotate(u);
				else rotate(x);
			}rotate(u);
		}
		return ;
	}
	inline void access(int u){
		int fir=u;
		for(int x=0;u;x=u,u=fa[u])splay(u),son[u][1]=x,update(u);
		splay(fir);
		return ;
	}
	inline void makeroot(int u){
		access(u),splay(u),tag[u]^=1;
		return ;
	}
	inline void split(int x,int y){
		makeroot(x),access(y),splay(y);
		return ;
	}
	inline int findroot(int u){
		access(u);
		while(son[u][0])u=son[u][0];
		splay(u);
		return u;
	}
	inline void cut(int x,int y){
		if((findroot(x)!=findroot(y)))return ;
		makeroot(x),access(y),splay(y);
		if(son[y][0]==x&&!son[x][1])fa[x]=son[y][0]=0,update(y);
		return ;
	}
}G;
int n,m;
signed main(){
	n=rd(),m=rd();
	for(int i=1;i<=n;i++)G.sum[i]=G.ans[i]=rd();
	for(int i=1;i<=m;i++){
		int k=rd(),x=rd(),y=rd();
		if(k==0)G.split(x,y),printf("%d\n",G.ans[y]);
		else if(k==1){
			G.makeroot(y);
			if(G.findroot(x)==y)continue;
			G.fa[y]=x;
		}
		else if(k==2)G.cut(x,y);
		else if(k==3)G.splay(x),G.sum[x]=y,G.update(x);
	}
	return 0;
}

lct的换根操作是通过splay的结构实现的,需要将u转到根后将整颗splay翻转。
同时由于lct的复杂度基于玄学的splay,如果splay少了可能会T,多转几下。(如findroot中的splay)

应用

挂个链接