前言
去年其实已经学过\(LCT\)了 ,但因为准备\(noip\)就没做什么题,忘得差不多啦,来补份总结
其实\(LCT\)是可以用\(FHQ\_treap\)实现的,但似乎更慢??某\(dalao\)测试过的
反正\(LCT\)码量小(稍微压点就\(60\)行以下了),而且网上大部分\(LCT\)都是用\(splay\)操作的,暂时不建议大家用\(FHQ\_treap\)
注:本文过于简短,只适合做总结观看
性质
\(LCT\),全称\(Link Cut Tree\),是一种维护动态树的利器
\(1.\)每个原树节点存在且仅存在于一棵\(splay\)
\(2.\)整个\(LCT\),是由多棵\(splay\)构成的森林,其中每棵\(splay\)维护的是一条从上至下在原树中深度递增的路径,中序遍历后的点序列的在原树中的深度递增
\(3.LCT\)里的点靠实边和虚边连接,实边在\(splay\)中
虚边是由一棵\(splay\)指向另一棵\(splay\)的某个节点:该\(splay\)中的根指向原树深度最小的点的父亲(根与深度最小的点不等价)
当原树某点有多个儿子时,其中一个儿子存在于同棵\(splay\)且拉实边,其他儿子存在于其他的\(splay\)向该点拉虚边
\(Update(x)\)更新\(x\)某个要维护的值
inline void Update(LL x){ sum[x]=val[x]^sum[son[x][0]]^sum[son[x][1]]; }
\(Notroot(x)(\)不该棵\(splay\)为根\()Splay(x)(\)上旋到该棵\(splay\)的根\(),Rotate(x)\)
这三个函数不建议大家改动,老老实实打就好了,容易出错 且与标准\(splay\)有部分差别
inline bool Notroot(LL x){ return son[fa[x]][0]==x||son[fa[x]][1]==x; } inline void Rotate(LL x){ LL y=fa[x],z=fa[y],lz=(son[y][1]==x);; if(Notroot(y)){ son[z][son[z][1]==y]=x;//** } son[y][lz]=son[x][lz^1],fa[son[y][lz]]=y; son[x][lz^1]=y,fa[y]=x; fa[x]=z; Update(y),Update(x); } inline void Splay(LL x){ LL y(x),top(0); sta[++top]=y;//** while(Notroot(y)) sta[++top]=y=fa[y];//** while(top) Pushdown(sta[top--]);//** while(Notroot(x)){ y=fa[x]; if(Notroot(y)){ LL z(fa[y]); if(((son[y][0]==x)^(son[z][0]==y))==0) Rotate(y); else Rotate(x); } Rotate(x); } }
\(Access(x)\)拉链1(维护\(x\)到原树根)
每次把节点\(x\)上旋到根,因为我们是要维护这一条链,当然比\(x\)深度更大的点(右子树)丢到另外的\(splay\)里去
细节:并不等效于拉一条以实根为根的链,故查询链时需注意直接\(Split(x,y)\)再查询\(y\)
inline void Access(LL x){ for(LL y=0;x;y=x,x=fa[x]){ Splay(x),son[x][1]=y,Update(x); } }
比如我们\(Access(N)\),看一下是怎么具体移动的
\(Makeroot(x)\)(换根)
通常我们维护的\((x,y)\)是位于原根的两个不同的子树,可是\(Access(x)\)只能维护\(x\)到原根这条链
一棵树随便提一点上来都还是树,那我们维护引入换根操作通过重构\(LCT\),使得满足原有性质上换原根
比如我们维护\((x,y)\)这条链,就\(Makeroot(x),Access(y)\)
来看看\(Makeroot(x)\)的具体代码:当我们把\(x\)上旋到根时是没有右子树的(\(x\)深度最大),而我们既然要换根得让\(x\)变成深度最小的,就交换左右子树
inline void Pushr(LL x){ swap(son[x][0],son[x][1]);r[x]^=1; } inline void Makeroot(LL x){ Access(x),Splay(x),Pushr(x); }
\(Pushdown(x)\)下传标记
inline void Pushdown(LL x){ if(r[x]){ if(son[x][0]){Pushr(son[x][0]);} if(son[x][1]){Pushr(son[x][1]);} r[x]=0; } }
\(Findroot(x)\)找根
inline LL Findroot(LL x){ Access(x),Splay(x); while(son[x][0]){ Pushdown(x),x=son[x][0]; }Splay(x); return x; }
\(Split(x,y)\)拉链2(维护\(x\)到\(y\))
inline void Split(LL x,LL y){ Makeroot(x),Access(y),Splay(y); }
\(Link(x,y)\)动态连边
inline void Link(LL x,LL y){ Makeroot(x); if(Findroot(y)!=x) fa[x]=y; }
\(Cut(x,y)\)动态删边
inline void Delet(LL x,LL y){ Split(x,y); if(son[y][0]!=x||son[x][1]) return;//注意这个地方 fa[x]=son[y][0]=0; Update(y); }
模板题
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<string> #include<cmath> #include<stack> using namespace std; typedef int LL; const LL maxn=1e6; inline LL Read(){ LL x(0),f(1);char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar(); return x*f; } LL n,m; LL fa[maxn],son[maxn][2],val[maxn],sum[maxn],r[maxn]; inline bool Notroot(LL x){ return son[fa[x]][0]==x||son[fa[x]][1]==x; } inline void Pushr(LL x){ swap(son[x][0],son[x][1]);r[x]^=1; } inline void Pushdown(LL x){ if(r[x]){ if(son[x][0]){Pushr(son[x][0]);} if(son[x][1]){Pushr(son[x][1]);} r[x]=0; } } inline void Update(LL x){ sum[x]=val[x]^sum[son[x][0]]^sum[son[x][1]]; } inline void Rotate(LL x){ LL y=fa[x],z=fa[y],lz=(son[y][1]==x);; if(Notroot(y)){ son[z][son[z][1]==y]=x;//** } son[y][lz]=son[x][lz^1],fa[son[y][lz]]=y; son[x][lz^1]=y,fa[y]=x; fa[x]=z; Update(y),Update(x); } LL sta[maxn]; inline void Splay(LL x){ LL y(x),top(0); sta[++top]=y;//** while(Notroot(y)) sta[++top]=y=fa[y];//** while(top) Pushdown(sta[top--]);//** while(Notroot(x)){ y=fa[x]; if(Notroot(y)){ LL z(fa[y]); if(((son[y][0]==x)^(son[z][0]==y))==0) Rotate(y); else Rotate(x); } Rotate(x); } } inline void Access(LL x){ for(LL y=0;x;y=x,x=fa[x]){ Splay(x),son[x][1]=y,Update(x); } } inline void Makeroot(LL x){ Access(x),Splay(x),Pushr(x); } inline LL Findroot(LL x){ Access(x),Splay(x); while(son[x][0]){ Pushdown(x),x=son[x][0]; }Splay(x); return x; } inline void Split(LL x,LL y){ Makeroot(x),Access(y),Splay(y); } inline void Link(LL x,LL y){ Makeroot(x); if(Findroot(y)!=x) fa[x]=y; } inline void Delet(LL x,LL y){ Split(x,y); if(son[y][0]!=x||son[x][1]) return; fa[x]=son[y][0]=0; Update(y); } int main(){ n=Read(),m=Read(); for(LL i=1;i<=n;++i) val[i]=Read(); while(m--){ LL op(Read()),x(Read()),y(Read()); if(op==0){Split(x,y),printf("%d\n",sum[y]);} else if(op==1){Link(x,y);} else if(op==2){Delet(x,y);} else{Splay(x),val[x]=y;Update(x);} } return 0; }
来源:https://www.cnblogs.com/y2823774827y/p/10322083.html