算法笔记——动态树

        动态树维护一个由若干无序的有根树组成的森林,支持对某个节点到根的路径操作,以及对某个节点的子树操作,还支持换根、加/减边、子树合并/分离等。Link-Cut Trees(LCT)是最常见的一种解决动态树问题的工具。

         LCT为虚实链剖分,虚实链式动态变化的,每个实链都由一棵伸展树维护,所有伸展树就像一个森林,由续编连接在一起组成一棵LCT

LCT性质

(1)一个节点到其子节点最多有一个实边,其它均为虚边。(虚边为单向维护父节点信息不维护子节点信息)

(2)每棵伸展树都维护一条按原树深度严格递增的实链

(3)每个节点都被包含且仅被包含在一棵伸展树中。

LCT伸展树之间的连接“认父不认子”,实链形成的伸展树的虚边一定连向原树中该实链的父节点,但该子节点并不一定为原树中父节点的直接儿子。

LCT具有动态变化性:

(1)LCT中的虚实边是动态变化的;

(2)伸展树也是动态变化的,可以随时旋转,只要满足原树中的节点深度有序即可。(即中序遍历输出单个伸展树中的节点,其在原树中的深度是有序的)

LCT基本操作

1、access(x)

        access(x)是动态树所有操作的基础,用于打通x到原树根节点的一条实链。即将x到树根的路径变为实链,这条实链上的节点与其他子节点的边变虚边。

操作过程如下

(1)将x旋转为所在伸展树的根,将其右儿子置空(变为虚边)。

(2)通过x向其他伸展树连的虚边得到父节点,将该节点旋转为所在伸展树的根,将右儿子置为前一个节点。

(3)重复第(2)步直至处理到根所在的伸展树。

void access(int x)
{
    for(int t=0;x;t=x;x=fa[x])
        splay(x),c[x][1]=t,update(x);
}

2、makeroot(x)

        access(x)只是打通x到原树根节点的一条实链,有时不能满足需要,很多时候都需要获取指定两个节点x,y之间的路径信息。然而x-y路径上的节点可能不在一棵伸展树中,不能按深度严格递增,此时需要换根操作,将指定的点x换成原树的根。

(1)access(x),打通一条x到原树根的实链;

(2)splay(x),将x旋转为所在伸展树的根。

(3)reverse(x),反转当前神转述的左右子树,使所有节点的深度倒过来(中序遍历从大到小)。

void makeroot(int x)
{
    access(x);splay(x);rev[x]^=1;
}

3、findroot(x)

        findroot(x)表示查找x所在原树的树根,主要用来判断两点之间的连通性。若findroot(x)==findroot(y),则表明x、y在同一棵树中。

        查找根的操作分为3步

(1)access(x),打通一条x到原树根的实链

(2)splay(x),将x旋转为所在伸展树的根

(3)查找当前伸展树的最左节点(深度最小的点,即树根),返回根节点即可

int findroot(int x)
{
    access(x);splay(x);
    while(c[x][0]) x=c[x][0];
    return x;
}

4、split(x,y)

        split(x,y)表示分离出x-y的路径为一条实链,用一个伸展树维护。

        分离操作分为3步

(1)makeroot(x),将x变成原树的根

(2)access(y),打通一条y到原树根的实链

(3)splay(y),将y旋转到当前伸展树的树根

void split(int x,int y)
{
    makeroot(x),access(y),splay(y);
}

5、link(x,y)

        link(x,y)表示在x,y之间连接一条边。若x,y之间连通,则不可以连边。连边操作分为2步

(1)makeroot(x),将x变为原树的根

(2)将x的父节点修改为y

void link(int x,int y)
{
    makeroot(x);fa[x]=y;
}

6、cut(x,y)

        cut(x,y)表示将x-y的边断开(删边)。若x,y之间不连通,则不可以删边。若x,y之间连通,则还要判断两者之间是否直接相连,若不是同样不可删。

        删边分为3步

(1)split(x,y),分李处x-y的路径为一条重链,若x不是y的左儿子或者x有右子树,则说明x与y之间有其他节点,不能删边

(2)将x-y的边断开,修改y的左儿子为0,y的左儿子x的父节点为0

(3)update(y),更新y的相关信息。

void cut(int x,int y)
{
    split(x,y);
    if(c[y][0]!=x || c[x][1])    return;
    c[y][0]=fa[x]=0;
    update(y);
}

7、isroot(x)

        isroot(x)表示判断x是否为所在伸展树的根。注意:伸展树的根和原树的根不是一回事。伸展树的根与其父节点是一条虚边,但并不代表其在原树上是父节点的左右儿子之一,但一定是其子树中的一个节点。

        只需要判断x的父节点的儿子信息是否为x即可(是否为虚边)

bool isroot(int x)
{
    return c[fa[x]][0]!=x && c[fa[x]][1]!=x;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值