LCT摘要
介绍、用途
LCT是树链剖分中的一种,又叫实链剖分、动态树,常用于维护动态的树、森林。
维护方式
LCT并不直接维护原树,而是用一堆splay作为辅助树来维护。原树中的一条实链上的点在一棵splay中,虚边体现为辅助上的连接两棵splay的虚边,只认爸爸不认儿子。
变量介绍
1 int n,m; 2 struct Node { 3 int fa,son[2]; //爸爸、儿子(0左1右) 4 int val,all; //该点权值、子树异或和 5 char ifz; //是否翻转(0否1是) 6 void res() { //重置(然并卵) 7 fa=son[0]=son[1]=val=0; 8 } 9 } tree[maxn]; 10 int pre[maxn],inp; //翻转序列(splay用)
各种操作
判断一个点是哪个儿子
不多说了
1 char which(int x) { 2 return x==tree[tree[x].fa].son[1]; 3 }
判断一个点是不是该splay的根
也不多说了
1 char isroot(int x) { 2 return x!=tree[tree[x].fa].son[which(x)]; 3 }
splay的操作
1 void rotate(int x) { 2 int f=tree[x].fa,ff=tree[f].fa,c=which(x); 3 if(!isroot(f)) tree[ff].son[which(f)]=x; //若它爸是根就不要搞它爷了 4 tree[x].fa=ff; 5 tree[f].son[c]=tree[x].son[c^1]; 6 tree[tree[f].son[c]].fa=f; 7 tree[x].son[c^1]=f; 8 tree[f].fa=x; 9 update(f); 10 update(x); 11 } 12 void splay(int x) { 13 int f; 14 pre[inp=1]=x; 15 for(f=x; !isroot(f); f=tree[f].fa) pre[++inp]=tree[f].fa; //挖出它到根的点 16 fdi(i,inp,1,1) pushdown(pre[i]); //全部pushdown 17 for(; !isroot(x); rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa); //无需pushdown 18 }
打通这个点到原树的根为实链
这个是重点!!!是LCT的核心!!!
首先,先将该节点splay到根,并将其爸爸splay到根。于是我们知道,它爸爸的右儿子深度大于它爸爸,是需要砍成虚边的点,而它的深度也大于它爸爸,所以直接将它爸爸的右儿子变成它。重复上述操作,直到它无爸爸。
1 void access(int x) { 2 for(int pr=0; x; pr=x,x=tree[x].fa)splay(x),tree[x].son[1]=pr,update(x); 3 }
将这个点变成原树的根
先打通这个点到根,并将它splay到根。然后我们可以发现,不在这棵splay上的点不受影响,而这棵splay上的点深浅颠倒,对应到splay上就是区间翻转。所以给它打上一个翻转标记。
1 void makeroot(int x) { 2 access(x); 3 splay(x); 4 tree[x].ifz^=1; //打翻转标记 5 }
查找这个点所在原树的根
先打通它到根并splay,然后找到它所在splay的最左边的点(即一直往左儿子找)。
1 int find(int x) { 2 for(access(x),splay(x); tree[x].son[0]; x=tree[x].son[0]); 3 return x; 4 }
连接个点并连接两棵树
将一个点变成根,并令这个点爸爸为另一个点。注意先判断这两个点在不在一棵树内,在就不用连了。
1 void link(int x,int y) { 2 makeroot(x); 3 tree[x].fa=y; 4 }
切断两点之间的边
先判断在不在一棵树内,不在就不切。然后将一个点变成根,另一个点打通到根并splay到根。易发现若这两个点间有边则这棵splay中只有它们俩。判断一下即可。
1 void cut(int x,int y) { 2 makeroot(x); 3 access(y); 4 splay(y); 5 if(tree[y].son[0]==x&&!tree[y].son[1]&&!tree[x].son[0]&&!tree[x].son[1])tree[y].son[0]=tree[x].fa=0; 6 }
改变一个点的值
将这个点变成根,并将其splay,再改变权值即可。
1 void change(int x,int y) { 2 makeroot(x); 3 splay(x); 4 tree[x].val=y; 5 update(x); 6 }
查询x到y的异或和
将x变成根,打通y并splay,直接查询即可。
1 int query(int x,int y) { 2 makeroot(x); 3 access(y); 4 splay(y); 5 return tree[y].all; 6 }
时空复杂度
时间复杂度
splay:均摊O(logn)的不用说了吧
access:由于每次access最多有logn条实边变成虚边,splay复杂度也仅为均摊O(logn),因此时间复杂度均摊O(logn)
makeroot:makeroot的开销主要为access,因此也为均摊O(logn)
其他:基于以上三种操作,因此都为均摊O(logn)
只是常数无比巨大!!!
只是常数无比巨大!!!
只是常数无比巨大!!!
空间复杂度
显然是O(n)的
题目
洛谷P3690 【模板】Link Cut Tree (动态树)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ImaxnF 0x7fffffff 4 #define ME 0x7f 5 #define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout) 6 #define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c)) 7 #define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c)) 8 #define fel(i,a) for(register int i=h[a];i;i=ne[i]) 9 #define ll long long 10 #define MEM(a,b) memset(a,b,sizeof(a)) 11 #define maxn (300000+10) 12 int n,m; 13 struct Node{ 14 int fa,son[2]; 15 int val,all;//,siz; 16 char ifz; 17 void res(){fa=son[0]=son[1]=val=/*siz=*/0;} 18 }tree[maxn]; 19 int pre[maxn],inp; 20 template<class T> 21 inline T read(T &n){ 22 n=0;int t=1;char ch; 23 for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-1:n=ch-'0'; 24 for(ch=getchar();isdigit(ch);ch=getchar()) n=n*10+ch-'0'; 25 return (n*=t); 26 } 27 template<class T> 28 T write(T n){ 29 if(n<0) putchar('-'),n=-n; 30 if(n>=10) write(n/10);putchar(n%10+'0'); 31 } 32 template<class T> 33 T writeln(T n){ 34 write(n);putchar('\n'); 35 } 36 char which(int x){return x==tree[tree[x].fa].son[1];} 37 char isroot(int x){return x!=tree[tree[x].fa].son[which(x)];} 38 void update(int x){tree[x].all=tree[tree[x].son[0]].all^tree[tree[x].son[1]].all^tree[x].val;} 39 void pushdown(int x){ 40 if(tree[x].ifz){ 41 tree[x].ifz=0,swap(tree[x].son[0],tree[x].son[1]); 42 tree[tree[x].son[0]].ifz^=1,tree[tree[x].son[1]].ifz^=1; 43 } 44 }void rotate(int x){ 45 int f=tree[x].fa,ff=tree[f].fa,c=which(x);if(!isroot(f)) tree[ff].son[which(f)]=x; 46 tree[x].fa=ff;tree[f].son[c]=tree[x].son[c^1];tree[tree[f].son[c]].fa=f; 47 tree[x].son[c^1]=f;tree[f].fa=x;update(f);update(x); 48 }void splay(int x){ 49 int f;pre[inp=1]=x;for(f=x;!isroot(f);f=tree[f].fa) pre[++inp]=tree[f].fa;fdi(i,inp,1,1) pushdown(pre[i]); 50 for(;!isroot(x);rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa);//update(x); 51 }void access(int x){for(int pr=0;x;pr=x,x=tree[x].fa)splay(x),tree[x].son[1]=pr,update(x);} 52 void makeroot(int x){access(x);splay(x);tree[x].ifz^=1;} 53 int find(int x){for(access(x),splay(x);tree[x].son[0];x=tree[x].son[0]);return x;} 54 void cut(int x,int y){makeroot(x);access(y);splay(y);if(tree[y].son[0]==x&&!tree[y].son[1]&&!tree[x].son[0]&&!tree[x].son[1]/*tree[y].siz==2*/)tree[y].son[0]=tree[x].fa=0;} 55 void link(int x,int y){makeroot(x);tree[x].fa=y;} 56 void change(int x,int y){makeroot(x);splay(x);tree[x].val=y;update(x);} 57 int query(int x,int y){makeroot(x);access(y);splay(y);return tree[y].all;} 58 int main(){ 59 read(n);read(m); 60 fui(i,1,n,1) tree[i].val=read(tree[i].all); 61 fui(i,1,m,1){ 62 int opt,x,y; 63 read(opt);read(x);read(y); 64 switch(opt){ 65 case 0:writeln(query(x,y));break; 66 case 1:if(find(x)!=find(y)) link(x,y);break; 67 case 2:if(find(x)==find(y)) cut(x,y);break; 68 case 3:change(x,y); 69 } 70 } 71 return 0; 72 }
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ImaxnF 0x7fffffff 4 #define ME 0x7f 5 #define FO(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout) 6 #define fui(i,a,b,c) for(int i=(a);i<=(b);i+=(c)) 7 #define fdi(i,a,b,c) for(int i=(a);i>=(b);i-=(c)) 8 #define fel(i,a) for(register int i=h[a];i;i=ne[i]) 9 #define ll long long 10 #define MEM(a,b) memset(a,b,sizeof(a)) 11 #define maxn (10000+10) 12 int n,m; 13 struct Node{ 14 int fa,son[2]; 15 char ifz; 16 }tree[maxn]; 17 int pre[maxn],inp; 18 template<class T> 19 inline T read(T &n){ 20 n=0;int t=1;double x=10;char ch; 21 for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());(ch=='-')?t=-1:n=ch-'0'; 22 for(ch=getchar();isdigit(ch);ch=getchar()) n=n*10+ch-'0'; 23 if(ch=='.') for(ch=getchar();isdigit(ch);ch=getchar()) n+=(ch-'0')/x,x*=10; 24 return (n*=t); 25 } 26 char which(int x){return x==tree[tree[x].fa].son[1];} 27 char isroot(int x){return x!=tree[tree[x].fa].son[which(x)];} 28 void pushdown(int x){ 29 if(tree[x].ifz){ 30 tree[x].ifz=0,swap(tree[x].son[0],tree[x].son[1]); 31 tree[tree[x].son[0]].ifz^=1,tree[tree[x].son[1]].ifz^=1; 32 } 33 } 34 void rotate(int x){ 35 int f=tree[x].fa,ff=tree[f].fa,c=which(x);if(!isroot(f)) tree[ff].son[which(f)]=x; 36 tree[x].fa=ff;tree[f].son[c]=tree[x].son[c^1];tree[tree[f].son[c]].fa=f; 37 tree[x].son[c^1]=f;tree[f].fa=x; 38 } 39 void splay(int x){ 40 int f;pre[inp=1]=x;for(f=x;!isroot(f);f=tree[f].fa) pre[++inp]=tree[f].fa;fdi(i,inp,1,1) pushdown(pre[i]); 41 for(;!isroot(x);rotate(x))if(!isroot(tree[x].fa))rotate((which(tree[x].fa)^which(x))?x:tree[x].fa); 42 } 43 void access(int x){for(int pr=0;x;pr=x,x=tree[x].fa)splay(x),tree[x].son[1]=pr;} 44 void makeroot(int x){access(x);splay(x);tree[x].ifz^=1;} 45 int find(int x){for(access(x),splay(x);tree[x].son[0];x=tree[x].son[0]);return x;} 46 void cut(int x,int y){makeroot(x);access(y);splay(y);if(tree[y].son[0]==x&&!tree[y].son[1]&&!tree[x].son[0]&&!tree[x].son[1])tree[y].son[0]=tree[x].fa=0;} 47 void link(int x,int y){makeroot(x);tree[x].fa=y;} 48 int main(){ 49 read(n);read(m); 50 fui(i,1,m,1){ 51 int x,y;char opt; 52 for(opt=getchar();opt!='Q'&&opt!='C'&&opt!='D';opt=getchar());read(x);read(y); 53 switch(opt){ 54 case 'Q':puts((find(x)==find(y))?"Yes":"No");break; 55 case 'C':if(find(x)!=find(y)) link(x,y);break; 56 case 'D':cut(x,y);break; 57 } 58 } 59 return 0; 60 }