Link-Cut Tree
把每个重链看成是一个splay
mroot:将x整到splay根上,将这个splay上下左右翻转
这个根的左儿子是他上面的重链,右儿子是他下面的重链
access:将根(最最上面)到x的边看成一条重链
link:连接两点都到根上,再更新
cut:切除两点关系,先将x转到根上,再把y到最最上面根的路变为重链,那么一定会经过x,然后将y旋到根上,删除关系即可。
find:将他翻到根然后找这棵树中深度最小的。
我一开始有两点疑惑1为什么mroot操作要左右翻转
因为我们翻转之前他的深度是最大的,但我们翻转之后他虽然在splay中表现出来的依旧深度最大但我们需要让他变为深度最小的,所以比他大的要全去右边。
第二个是我们为什么要让他变为深度最小的
因为我们在cut和link操作中依照splay的性质将y转到根上然后让x做他的左儿子条件是x是深度最小的,
而且link操作里你连边相当于把两棵树接在一起,如果这两个点中没有一个点是根节点的话那么这个点就会有两个父亲从而不满足树的形态。
在find操作里我们只需要找比他小的所以没有必要加。或者你可以sb一样的加reverse然后splay到根再找根的右儿子的最左儿子。
By:大奕哥
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e5+10; 4 int n,m,fa[N],c[N][2],st[N]; 5 bool rev[N];char s[10]; 6 void pushdown(int x) 7 { 8 if(rev[x]) 9 { 10 rev[c[x][0]]^=1;rev[c[x][1]]^=1; 11 swap(c[x][0],c[x][1]);rev[x]=0; 12 } 13 } 14 inline bool isroot(int x) 15 { 16 return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; 17 } 18 void rotate(int x) 19 { 20 int y=fa[x],z=fa[y],l,r; 21 l=c[y][1]==x;r=l^1; 22 if(!isroot(y))c[z][c[z][1]==y]=x; 23 fa[x]=z;fa[y]=x;fa[c[x][r]]=y; 24 c[y][l]=c[x][r];c[x][r]=y; 25 } 26 void splay(int x) 27 { 28 int top=0;int i; 29 for(i=x;!isroot(i);i=fa[i])st[++top]=i; 30 st[++top]=i; 31 for(i=top;i;--i)pushdown(st[i]); 32 while(!isroot(x)) 33 { 34 int y=fa[x],z=fa[y]; 35 if(!isroot(y)) 36 { 37 if(c[y][0]==x^c[z][0]==y)rotate(x); 38 else rotate(y); 39 } 40 rotate(x); 41 } 42 } 43 void access(int x) 44 { 45 int y=0; 46 while(x) 47 { 48 splay(x); 49 c[x][1]=y; 50 y=x;x=fa[x]; 51 } 52 } 53 void mroot(int x) 54 { 55 access(x);splay(x);rev[x]^=1; 56 } 57 void link(int x,int y) 58 { 59 mroot(x);fa[x]=y;splay(x); 60 } 61 void cut(int x,int y) 62 { 63 mroot(x);access(y);splay(y);c[y][0]=fa[x]=0; 64 } 65 int find(int x) 66 { 67 access(x);splay(x); 68 int y=x; 69 while(c[y][0])y=c[y][0]; 70 return y; 71 } 72 int main() 73 { 74 scanf("%d%d",&n,&m);int x,y; 75 for(int i=1;i<=m;++i) 76 { 77 scanf("%s%d%d",s,&x,&y); 78 if(s[0]=='C')link(x,y); 79 else if(s[0]=='D')cut(x,y); 80 else{ 81 if(find(x)==find(y))puts("Yes"); 82 else puts("No"); 83 } 84 } 85 return 0; 86 }