传送门
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
",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;
}