LCT,link-cut-tree,一种基于splay的高级数据结构,常用于维护动态森林问题,但ta只能维护子树信息,无法修改子树信息。
首先,如果你不会splay,来这里看看吧。
接下来步入正题。
首先阐述一下个人对LCT的理解,其实你可以把LCT理解成许多棵splay,每一个联通块是一棵大splay,每个大splay中有许多的小splay,小splay的根靠虚边连上大splay,虚边是什么?即假设你从x向y连一条虚边,便只要将x父亲设为y,不用将y的儿子设为x。
但每一棵小splay中的边都是正常的树边。那么,判断一个点是不是splay的根,就要这样
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
那么,每棵小splay中的到底什么?你可以将其理解为你查询的历史路径。因为每当你要统计一条路径上的信息,就要用到access操作
inline void access(int x)
{
for(register int y=0;x;y=x,x=fa[x])
{
splay(x);ch[x][1]=y;push_up(x);
}
}
access看英文就知道,这个函数相当于打通了一条从该节点到root的通道,每次将这个点旋到当前splay的root,再将以前那一棵的root接到这一棵的右儿子上,就这样一路旋上去,最后,这个点会停在主splay的最下面。
inline void makeroot(int x)
{
access(x);splay(x);rev[x]^=1;
}
makeroot,顾名思义,把这个节点作为根,为什么要打翻转标记?因为LCT维护每个点的相对深度,当你把一个点旋到root,例如是一条链,你把点从最右边旋上来,那么本来这个点应该是最深的,现在变成最浅的了,所以打上翻转标记,使他依然是最深的。
当你要统计x到y路径上的某些信息是,只需要makeroot(x),access(y),这时,x到y中间的点就是你要的路径,再splay(y),y旋上去时push_up就会把路径上的信息统计掉,所以ans就是y点信息了。
int l[N];
inline void splay(int x)
{
l[0]=0;
int y=x;
while(1)
{
l[++l[0]]=y;
if(isroot(y))break;
y=fa[y];
}
Fordown(i,l[0],1)push_down(l[i]);
while(!isroot(x))
{
//if(!isroot(fa[x]))rotate(get(x)^get(fa[x])?x:fa[x]);
rotate(x);
}
}
这里的splay,因为有翻转操作,所以要先把路径上所有点入队,从上面开始依次下放翻转标记
inline int find(int x)
{
access(x);splay(x);
while(ch[x][0])x=ch[x][0];
return x;
}
find操作,其实就是找到左边的点,用来判断两个点在不在一个联通块中
inline void link(int x,int y)
{
if(find(x)==find(y))return;
makeroot(x);
fa[x]=y;
}
inline void cut(int x,int y)
{
makeroot(x);
access(y);splay(y);
if(ch[y][0]==x)ch[y][0]=0,fa[x]=0;
}
如果你弄清楚了前面几个操作,那么link和cut操作就很简单了
最后,在rotate的时候,如果你需要维护路径上的信息就需要push_up,但因为题目的不同而push_up的东西不一样,所以要根据题目来定,这里就拿维护路径上的^值,所以这样写
inline void push_up(int x){sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];}
下面是例题
洛谷LCT模板(3690)要就维护路径^值
#include<bits/stdc++.h>
using namespace std;
typedef int sign;
typedef long long ll;
#define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i)
#define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i)
const int N=3e5+5;
bool cmax(sign &a,sign b){return (a<b)?a=b,1:0;}
bool cmin(sign &a,sign b){return (a>b)?a=b,1:0;}
template<typename T>T read()
{
T ans=0,f=1;
char ch=getchar();
while(!isdigit(ch)&&ch!=\'-\')ch=getchar();
if(ch==\'-\')f=-1,ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch-\'0\'),ch=getchar();
return ans*f;
}
void file()
{
#ifndef ONLINE_JUDGE
freopen("LCT.in","r",stdin);
freopen("LCT.out","w",stdout);
#endif
}
int n,m,val[N],sum[N];
int ch[N][2],fa[N],rev[N];
inline void push_up(int x){sum[x]=val[x]^sum[ch[x][0]]^sum[ch[x][1]];}
inline void push_down(int x)
{
if(rev[x])
{
rev[ch[x][0]]^=1;
rev[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);
rev[x]=0;
}
}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline bool get(int x){return x==ch[fa[x]][1];}
inline void rotate(int x)
{
int old=fa[x],oldfa=fa[old],o=get(x);
if(!isroot(old))ch[oldfa][get(old)]=x;
fa[x]=oldfa;fa[ch[x][o^1]]=old;fa[old]=x;
ch[old][o]=ch[x][o^1];ch[x][o^1]=old;
push_up(old);push_up(x);
}
int l[N];
inline void splay(int x)
{
l[0]=0;
int y=x;
while(1)
{
l[++l[0]]=y;
if(isroot(y))break;
y=fa[y];
}
Fordown(i,l[0],1)push_down(l[i]);
while(!isroot(x))
{
if(!isroot(fa[x]))rotate(get(x)^get(fa[x])?x:fa[x]);
rotate(x);
}
}
inline void access(int x)
{
for(register int y=0;x;y=x,x=fa[x])
{
splay(x);ch[x][1]=y;push_up(x);
}
}
inline void makeroot(int x)
{
access(x);splay(x);rev[x]^=1;
}
inline int find(int x)
{
access(x);splay(x);
while(ch[x][0])x=ch[x][0];
return x;
}
inline void link(int x,int y)
{
if(find(x)==find(y))return;
makeroot(x);
fa[x]=y;
}
inline void cut(int x,int y)
{
makeroot(x);
access(y);splay(y);
if(ch[y][0]==x)ch[y][0]=0,fa[x]=0;
}
void work()
{
while(m--)
{
static int opt,x,y;
opt=read<int>();x=read<int>();y=read<int>();
if(opt==0)
{
makeroot(x);
access(y);splay(y);
printf("%d\\n",sum[y]);
}
else if(opt==1)link(x,y);
else if(opt==2)cut(x,y);
else if(opt==3)
{
access(x);splay(x);
val[x]=y;push_up(x);
}
}
}
inline void input()
{
n=read<int>();m=read<int>();
For(i,1,n)sum[i]=val[i]=read<int>();
}
int main()
{
file();
input();
work();
return 0;
}