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

传送门
Link/Cut Tree 板子题。就当存个模板吧。
说一下对 LCT 的简单理解吧,对于一颗树,将其分为多个 splay,每个 splay 之间通过虚边联系,每个 splay 不重叠的维护树的一部分边。通过 access 操作,虚边变实边,实边变虚边,可以构造出一个 splay 维护从指定节点到树根的路径上的点的信息。而 makeroot 操作可以更换树的根节点。基本通过这两个操作就可以快速维护指定的一条链。至于其他的辅助函数就康康代码了。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct LCT{
	int fa[N],ch[N][2],val[N],xorv[N],rev[N],tot;
	//ident、connect都是splay的经典操作 
	bool ident(int p,int f){return ch[f][1]==p;}
	void connect(int p,int f,int k){ch[f][k]=p;fa[p]=f;}
	//判断点p是否是某一颗splay的根,即p与fa[p]之间的边是否是虚边 
	bool ntroot(int p){return ch[fa[p]][0]==p||ch[fa[p]][1]==p;}
	//pushup常规操作 
	void pushup(int p){xorv[p]=xorv[ch[p][0]]^xorv[ch[p][1]]^val[p];}
	//pushdown操作是反转序列,为换树根操作铺垫。 
	void pushdown(int p){
		if(!rev[p])return;
		swap(ch[p][0],ch[p][1]);
		if(ch[p][0]) rev[ch[p][0]]^=1;
		if(ch[p][1]) rev[ch[p][1]]^=1;
		rev[p]=0;
	}
	//旋转基本操作,要判断一下f是不是根,因为虚边的处理。 
	void rotate(int p){
		int f=fa[p],ff=fa[f],k=ident(p,f);
		connect(ch[p][k^1],f,k);
		fa[p]=ff;
		if(ntroot(f)) ch[ff][ident(f,ff)]=p;
		connect(f,p,k^1);
		pushup(f),pushup(p);
	}
	//伸展操作,将p伸展到其所处的splay的根上,注意先把路径上的标记下传。 
	void pushall(int p){if(ntroot(p)) pushall(fa[p]);pushdown(p);}
	void splay(int p){
		pushall(p);
		while(ntroot(p)){
			int f=fa[p],ff=fa[f];
			if(ntroot(f)) ident(p,f)^ident(f,ff)?rotate(p):rotate(f);
			rotate(p);
		}
	}
	//将从p到树根上的边全部转化为实边 
	//其实就是把p到树根之间的所有点构成一颗splay,且不包含其他节点。splay的根是不确定的。 
	void access(int p){for(int t=0;p;t=p,p=fa[p]) splay(p),ch[p][1]=t,pushup(p);}
	//将p设为树根 
	void makert(int p){access(p);splay(p);rev[p]^=1;}
	//找到p所处的树的树根 
	int findrt(int p){access(p);splay(p);while(ch[p][0]) p=ch[p][0];splay(p);return p;}
	//连接点p和点q,只需特判p与q是否在同一颗树上即可 
	void link(int p,int q){makert(p);fa[p]=q;}
	//断开p与q的边,需判断p与q是否在同一颗树上和p与q之间是否有边。
	//先把p设为树根,再把p、q之间的点拉到splay里,
	//然后把q转到根,如果p此时是q的左儿子,且p没有右儿子,则p、q有边。 
	void cut(int p,int q){makert(p);access(q);splay(q);if(ch[q][0]==p&&!ch[p][1])ch[q][0]=fa[p]=0;}
}lct;

int n,m;

int main(){
	scanf("%d%d",&n,&m);
	lct.tot=n;
	for(int i=1;i<=n;i++) scanf("%d",&lct.val[i]),lct.xorv[i]=lct.val[i];
	for(int i=1,opt,x,y;i<=m;i++){
		scanf("%d%d%d",&opt,&x,&y);
		if(opt==0) {
			lct.makert(x);lct.access(y);lct.splay(y);
			printf("%d\n",lct.xorv[y]);
		}
		else if(opt==1){
			if(lct.findrt(x)!=lct.findrt(y)) lct.link(x,y);
		}
		else if(opt==2){
			if(lct.findrt(x)==lct.findrt(y)) lct.cut(x,y);
		}
		else if(opt==3){
			lct.access(x);lct.splay(x);lct.val[x]=y;lct.pushup(x);
		}
	}
	return 0;
}
posted @ 2020-04-15 16:22  BakaCirno  阅读(119)  评论(0编辑  收藏  举报