安利大佬的讲解:
LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
题目传送:【模板】Link Cut Tree (动态树)(luogu)
Description
写一种数据结构,支持更改节点权值,连边,删边,查询两点间路径上节点权值异或和
Solution
讲解大佬已经讲得很明白了
此处补充一点容易蒟蒻认为跳坑的细节(以他的代码为例)
- fa [ ]储存的是原树上的父亲信息,但在一个splay中是打乱的,只能确定每个splay中序遍历第一点的原树父亲是fa[ 本棵splay树的根 ]
- link( x , y )中必须写 f [ x ]=y,而不能写 f[ y ]=x,因为y可能已经存储了splay上的父亲信息,而x为splay树的根
- 注意splay函数和rotate函数和传统写法的不同,LCT中对0的处理更苛刻
Code本人模板(强行往自己的传统写法上靠...)
#include <cstdio> #include <cstdlib> #include <algorithm> using namespace std; const int N=1e5+10; int fa[N],ch[N][2]; int s[N],d[N],n,m,opt,x,y,rev[N]; bool nroot(int x) { return x==ch[fa[x]][0] || x==ch[fa[x]][1]; } int get(int x) { return x==ch[fa[x]][1]; } void push_up(int x) { s[x]=s[ch[x][0]]^s[ch[x][1]]^d[x]; } void change(int x) { swap(ch[x][0],ch[x][1]); rev[x]^=1; } void push_down(int x) { if(rev[x]) { change(ch[x][0]); change(ch[x][1]); rev[x]=0; } } void dfs(int x) { if(nroot(x)) dfs(fa[x]); push_down(x); } void rotate(int x) { int y=fa[x],z=fa[y],wh=get(x); if(nroot(y)) ch[z][get(y)]=x; if(ch[x][wh^1]) fa[ch[x][wh^1]]=y; ch[y][wh]=ch[x][wh^1]; ch[x][wh^1]=y; fa[y]=x,fa[x]=z; push_up(y),push_up(x); } void splay(int x) { dfs(x); for(int fx;fx=fa[x],nroot(x);rotate(x)) if(nroot(fx)) rotate(get(x)==get(fx)?fx:x); } void access(int x) { for(int y=0;x;x=fa[y=x]) splay(x),ch[x][1]=y,push_up(x); } void makeroot(int x) { access(x),splay(x); change(x); } int findroot(int x) { access(x),splay(x); while(ch[x][0]) push_down(x),x=ch[x][0]; splay(x); return x; } 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; } void split(int x,int y) { makeroot(x); access(y); splay(y); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&d[i]); while(m--) { scanf("%d%d%d",&opt,&x,&y); if(opt==0) split(x,y),printf("%d\n",s[y]); else if(opt==1) link(x,y); else if(opt==2) cut(x,y); else splay(x),d[x]=y,push_up(x); } return 0; }