作用
一种动态树,用于维护森林,支持加边,删边,换根等操作,每次操作的复杂度均为log.
做法
将每棵树分成多条实链,实链之间用虚边连接,要求每一条实链不存在深度相同的点,之后每一条实链再用splay维护,并且维护时保证每个splay维护的子树的中序遍历深度递增,这样就可以动态地修改虚边和实边了.
核心操作:access
将某个点到根的路径变为实边,可以发现到根的路径将会经过多个splay,每次要将两个splay合并时都要将一条虚边改为实边,那么为了保证上述性质,可能要将一条实边换成虚边,每次将要连的点splay到实链的根后再修改,用循环来写还是比较短的.
inline void acc(int u)
{
int p,q;
for(p=u,q=0; p; q=p,p=fa[p])
{
splay(p);
down(p);
son[p][1]=q;
up(p);
}
}
make_root
若要将点p作为整棵树的根,首先access§,可以发现,此时p所在的实链中p是深度最大的点,因为中序遍历深度恰好为递增,若将中序遍历翻转也就是交换所有点的左右孩子,就可以将p变为深度最小的点,也就是整棵树的根,只要 s p l a y ( p ) splay(p) splay(p)后给p打上反转标记即可.
inline void make_root(int u)
{
acc(u);
splay(u);
fz[u]^=1;
}
find_root
要找到p所在树的根只要先 a c c e s s ( p ) access(p) access(p)后 s p l a y ( p ) splay(p) splay(p),只要沿着p的左孩子一直找下去就是该树深度最小的一个点,也就是整棵树的根
inline int find_root(int u)
{
acc(u),splay(u),down(u);
for(; son[u][0]; u=son[u][0],down(u));
return u;
}
需要注意的是这样find_root最劣会变成O(n)的,因此如果要判断两个点事否在同一个联通块中时,可以用如下代码
inline bool ask(int u,int v)
{
make_root(u),make_root(v);
for(;u;u=fa[u])
{
if(u==v) return 1;
}
return 0;
}
split
提取出u到v的链,只要将u作为根节点后,access(v),splay(v),那么就可以提取出这条链了.
inline void spl(int u,int v)
{
make_root(u);
acc(v),splay(v);
}
link
将u,v两条边连起来,首先用find_root是否相同来判断连通性,即是否要连边,之后不能直接该父节点,因为父节点记录了splay上的父节点,所以要将u变为根后再将fa[u]改为v.
inline void link(int u,int v)
{
if(find_root(u)==find_root(v)) return;
make_root(u),fa[u]=v;
}
cut
切断u,v间的边,首先split(u,v),提取出这条链,之后判断一下u是否恰好为v的左孩子,如果是,则修改u的父节点和v的左孩子即可.
inline void cut(int u,int v)
{
spl(u,v);
down(v),down(u);
if(son[v][0]!=u || son[u][1]) return;
fa[u]=son[v][0]=0;
up(v);
}
代码(以P3690 【模板】Link Cut Tree (动态树)为例)
#include<iostream>
#include<cstdio>
#define N 300100
using namespace std;
int n,m,tt,dn[N],fa[N],son[N][2],xo[N];
bool fz[N];
inline bool as(int u){return son[fa[u]][1]==u;}
inline bool ar(int u){return son[fa[u]][0]!=u && son[fa[u]][1]!=u;}
inline void up(int u){if(u) xo[u]=xo[son[u][0]]^xo[son[u][1]]^dn[u];}
inline void down(int u)
{
if(fz[u])
{
swap(son[u][0],son[u][1]);
fz[son[u][0]]^=1;
fz[son[u][1]]^=1;
fz[u]=0;
}
}
inline void rot(int u)
{
down(fa[u]),down(u);
int p=fa[u],d=as(u);
if(!ar(p))
{
son[fa[p]][as(p)]=u;
}
fa[u]=fa[p];
fa[p]=u;
fa[son[u][!d]]=p;
son[p][d]=son[u][!d];
son[u][!d]=p;
up(p),up(u);
}
inline void splay(int u)
{
int p;
for(; !ar(u);)
{
p=fa[u];
if(!ar(p))
as(u)==as(p)?rot(p):rot(u);
rot(u);
}
}
inline void acc(int u)
{
int p,q;
for(p=u,q=0; p; q=p,p=fa[p])
{
splay(p);
down(p);
son[p][1]=q;
up(p);
}
}
inline void make_root(int u)
{
acc(u);
splay(u);
fz[u]^=1;
}
inline int find_root(int u)
{
acc(u),splay(u),down(u);
for(; son[u][0]; u=son[u][0],down(u));
return u;
}
inline void spl(int u,int v)
{
make_root(u);
acc(v),splay(v);
}
inline void link(int u,int v)
{
if(find_root(u)==find_root(v)) return;
make_root(u),fa[u]=v;
}
inline void cut(int u,int v)
{
spl(u,v);
down(v),down(u);
if(son[v][0]!=u || son[u][1]) return;
fa[u]=son[v][0]=0;
up(v);
}
int main()
{
int i,j,o,p,q;
cin>>n>>m;
for(i=1; i<=n; i++)
{
scanf("%d",&dn[i]);
xo[i]=dn[i];
}
for(i=1; i<=m; i++)
{
scanf("%d%d%d",&o,&p,&q);
if(!o) spl(p,q),printf("%d\n",xo[q]);
else if(o==1) link(p,q);
else if(o==2) cut(p,q);
else acc(p),splay(p),dn[p]=q,up(p);
}
}