Link Cut Tree学习小记

前言

其实我早就知道Link Cut Tree 是什么东东,可是懒癌晚期不想仔细学。
简单点说就是动态的树链剖分,因为我们链剖用的是线段树,死在那里。
既然要用动态,那么肯定要用平衡树来维护啦,我们选择splay。
如果不会splay的还是别看了,splay学习小记

一些东西

其实这个东西杨哲的集训队作业也是有的,大家可以先去看一看。
我们把之前访问到的点称作偏爱点,从根到偏爱点的路径叫做偏爱路径。偏爱路径上的边叫做偏爱边。每个点向它的儿子只会连一条偏爱边。
我们用splay维护所有的偏爱路径,这样就形成了一片森林。
splay中点x保证左节点的深度小于x,右节点的深度大于x。
然后,对于每个splay中的根,它在原树中往上第一条不是偏爱边所连向的点叫做它的Path Parent,连接这两个点的边叫做Path Parent边。
这样就形成了一棵LinkCutTree。
这里写图片描述
盗图狗路过

功能

要实现LinkCutTree的功能,我们就必须让那些点在同一棵splay上。
也就是说,我们需要用access(x)来把原树的根到x点的路径上所有的边变成偏爱边。

void access(int x){
    int y=0;
    while(x){
        splay(x,0);
        f[t[x][1]]=0,pfa[t[x][1]]=x;
        t[x][1]=y,f[y]=x;
        pfa[y]=0;
        update(x);
        y=x,x=pfa[x];
    }
}

其实就是沿着path parent边往上跳。
为什么要把右边断掉?
因为根到x中的点的深度都比x小,比x大的就不在偏爱路径上了。
这个操作是LinkCutTree的核心。
不过注意,执行splay操作的时候可能会改变pfa(因为这一棵splay的根改变了),所以旋转的时候要注意。
那些要维护的值就和普通splay一样维护就可以了。

接下来要讲的就是关于动态树的动态操作

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

这里就是我们要选用splay的原因。
因为我们有翻转操作。
为什么呢?
因为splay是按深度来建的,把x换到根之后,1~x的深度全部翻转了。

连边
void link(int x,int y) {
    makeroot(x);p[x]=y;
}

很显然吧。

删边
void cut(int x,int y) {
    makeroot(x);access(y);splay(y,0);
    t[y][0]=0;f[x]=p[x]=0;
    updata(y);
}

这个比较巧妙。
先把x换到根,然后把x到y的路径改成偏爱路径。
这样x和y就在同一棵splay上了。且x比y的深度小。
如果把y splay上去,x就会在y的左边。
断掉就好了。

求x到y的路径
makeroot(x);access(y);splay(y,0);

这样y点的值就是x到y的路径上的值。
原因一样。

然后这些就够用了吧。

哦,还有一点。
如何用LinkCutTree维护边权?
一个直观的想法是把每个点的点权设为它父边的边权。
但是这样很明显会错。
因为在access操作中树的形态会改变。
那么怎么办呢?

某神犇:把每条边看成点,维护点权就好了

肿么就辣么机智勒。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值