您的位置:首页 > 其它

Link Cut Tree学习小记

2016-09-03 16:09 323 查看

前言

其实我早就知道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操作中树的形态会改变。

那么怎么办呢?

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

肿么就辣么机智勒。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: