LCT

作用

一种动态树,用于维护森林,支持加边,删边,换根等操作,每次操作的复杂度均为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);
        son[p][1]=q;
        up(p);
    }
}

make_root

若要将点p作为整棵树的根,首先access(p),可以发现,此时p所在的实链中p是深度最大的点,因为中序遍历深度恰好为递增,若将中序遍历翻转也就是交换所有点的左右孩子,就可以将p变为深度最小的点,也就是整棵树的根,只要splay(p)后给p打上反转标记即可.

inline void make_root(int u)
{
    acc(u);
    splay(u);
    fz[u]^=1;
}

find_root

要找到p所在树的根只要先access(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);
}

将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);
    }
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/79881605
LCT