Link-Cut Tree

Link-Cut Tree(LCT)是一种高效解决动态树问题的数据结构。通过Access操作访问节点,并利用Prefered Edge和Prefered Child进行维护。LCT的核心包括Link(x,y)和Cut(x,y)操作,以及实现Access、获取路径和查找最近公共祖先(LCA)等功能。其效率为O((n+Q) * log2(n)),但常数较大。" 115246139,10819800,程序员必备:技术交流、资源获取与自我提升网址大全,"['编程语言', '程序人生', '面试', '资源分享']
摘要由CSDN通过智能技术生成

ps:推荐论文《QTREE解法的一些研究》by杨哲。

作用

高效解决动态树问题。

思想

Link-Cut Tree(简称LCT)将树的边分为Prefered Edge和普通边,将点分为Prefered Child和普通点,Prefered Edge和Prefered Child是不断变化的,且刚开始只有普通边和普通点。LCT的核心操作Access(x):访问x。

假设最后访问了lst节点,对于节点x,如果lst在x的儿子son的子树中,则son变成x的Prefered Child(原先的Prefered Child变为普通点),连接x和son的边变成Prefered Edge(原先的Prefered Edge变为普通边,由此可见x至多只有一个Prefered Child)。特殊的是,lst没有Prefered Child。那么显然访问了lst之后,lst到ro(ro表示lst所在树的根)之间的边都变成Prefered Edge了。

对于一条由Prefered Edge连接而成的链,我们用数据结构(通常用Splay,因为Splay可以方便的分裂和合并)来维护,将链自顶向下看做一个序列(dep越小越靠前)用Splay储存,且这条链的Splay的根节点储存了一个Path Father,表示这条链顶端节点的father。要注意,这个关系是单向的,也就是说这条链的Splay的根节点的father是Path Father,但Path Father所在Splay和这条链的Splay是不连通的。

假设我们已经实现了Access,那么如何实现Link(x,y)(把y连接到x上)和Cut(x,y)(把y从x上断开)呢?首先我们需要实现一个make_ro(p),作用是把p变成p所在树(注意,不是p所在Splay!)的根。代码如下:

void make_ro(int p) {Access(p);Splay(p);Add_flip(p);}

非常简短,但是好像很费解,我们来看图:
这里写图片描述
先访问p,这样p到ro之间的边就都变成Prefered Edge了(也就是说p到ro之间的点都在同一个Splay中了),然后把p伸展到p所在Splay的根。由于p是最深的节点,所以其他点均在p的左边,但是p成为了根节点,这条链就被翻转了,加翻转标记。完成这三个操作之后,p就成为了新根。

有了make_ro操作,Link和Cut就很简单了:

void Link(int x,int y) {Access(y);make_ro(y);father[y]=x;}

先把y变成根,然后把father[y]给x就行了(不要把son[x][0/1]给y,因为这是单向连接)。

void Cut(int x,int y)
{
    make_ro(y);Access(x);Splay(x);
    father[y]=son[x][0]=0;Pushup(x);
}

先把y变成根,然后访问x,访问之后x所在链中就只有x和y两个节点了,伸展x,则y必定是x的左二子。断开连接,更新x。

然而Access还没讲,其实非常暴力,假设目前处理到p,上次处理了lst。那么先断开p与原先Prefered Child之间的连接,然后连接p和lst,更新p。重复这个过程直到p为空。

void Access(int p)
{
    int lst=0;
    while (p)
    {
        Splay(p);son[p][1]=lst;Pushup(p);
        //先Splay(p),这样son[p][1]就是原先p的Prefered Child的子树了
        //将son[p][1]给成lst,更新p
        //不用修正father[son[p][1]],因为p成为了father[son[p][1]]的Path Father
        lst=p;p=father[p]; //继续处理
    }
}

这样不会超时吗?之后再讲。

除了Link和Cut,我们还可以实现以下功能:
1.取出x->y的路径
make_ro(x),Access(y),此时x->y的路径被存在同一棵Splay中。
2.求x和y的LCA
Access(x),Access(y),Splay(x),则LCA=x的Path Father。
特殊:当x没有Path Father时,LCA=x(因为y跨越了x到ro,所以x没有Path Father)。

效率

Splay的总效率为 O((n+Q)log2(n)) ,但是Access的总效率呢?
Access的效率取决于Prefered Child改变的次数,而Prefered Child改变的次数取决于Prefered Edge改变的次数。Prefered Edge和普通边不禁让我们想到了树链剖分-轻重链剖分中的重边和轻边。根据轻重链剖分的定义,我们会发现每次Access,至多有 log2(n) 条普通边变成了轻的Prefered Edge,也就是说普通边变成轻的Prefered Edge的总次数为 (n+Q)log2(n) 。但是有多少普通边变成了重的Prefered Edge呢?我们转化一下,普通边变成重的Prefered Edge的次数=重的Prefered Edge变成普通边的次数+n(可能最后全是重的Prefered Edge),而重的Prefered Edge变成普通边必定意味着一条普通边变成了轻的Prefered Edge,所以普通边变成重的Prefered Edge的次数= (n+Q)log2(n)+n
综上所述Access的总效率为 O((n+Q)log2(n)) ,也就是说LCT的总效率为 O((n+Q)log2(n))
ps:然而Splay常数巨大,所以LCT常数巨大……

模板题

BZOJ2002题解传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值