【Learning】一步步地解释Link-cut Tree
简介
Link-cut Tree,简称LCT。
干什么的?它是树链剖分的升级版,可以看做是动态的树剖。
树剖专攻静态树问题;LCT专攻动态树问题,因为此时的树剖面对动态树问题已经无能为力了(动态树问题通常夹杂着树的操作,如删边与连边。这是线段树无法应对的)。
LCT难写吗?不难写啊...
预备知识:Splay(没有接触也没有关系,打一打LCT也差不多懂了)、树链剖分。
1. LCT概念
树链剖分把树分成若干条重链,对于每条重链,用线段树来维护信息。利用各线段树的信息来得到答案。
模仿一下:
- LCT把树分成若干条重链:
这是假的重链!树剖是挑选重儿子来延续重链;而LCT的重链是随缘的......
我们先不管这里的重链是怎么确定的,因为在LCT中,重链是可以随时更改的!
- $access(u)$,这是我们的更改操作。作用是将$u$到原树根节点的一路都变成重链,同时,经过节点将会与原本属于的重链断开,如图,这是我们要维护的原树:
- 对于每条重链,我们用一棵Splay来维护信息,利用各Splay的信息来得到答案。
2. 存储方式
LCT是怎么存储的?其中涉及到的$access$操作可能会对Splay进行删点或加点......
我们的每条重链的Splay,都是连在一起的,但又是相互独立的!看图:
橙色边为每棵Splay,灰色边表示的是Splay之间的连接边。
每棵Splay储存照常,Splay的中序遍历即重链节点从浅到深的排列。每棵Splay内节点的关系可能和原树不同,但是与其他Splay连边的节点没有改变。
由此,每棵Splay可以维护一条链上的信息。
但只有每棵Splay的根节点能连向其他Splay的某个节点(灰色边)。Splay根节点$root$记录它的父亲是谁(有的Splay根节点$root$没有父亲),而它的父亲并不记录自己有这个儿子$root$。
发现,每一个节点,都能够通过一直走父亲(不管是亲生还是认领的),走到某一个点,这个点就是上节提到的原树根节点,不同于Splay的根节点。
3. 基础函数(以下基本都是经典函数)
我们需要一个函数来判断当前节点$u$是否为所属Splay的根节点:
bool isroot(int u){
return ch[fa[u]][]!=u&&ch[fa[u]][]!=u;
}
即父亲的左右儿子都不是自己,说明此节点是Splay的根节点,它的父亲并不记录自己。
需要一个函数判断当前节点$u$是父亲节点的左儿子还是右儿子:
int who(int u){
return ch[fa[u]][]==u;
}
如果是左儿子,返回0;否则返回1。
更新Splay信息函数,作用是收集左右儿子的信息。这里以最大值举例:
void update(int u){
if(!u) return;
info[u]=max(w[u],max(info[ch[u][]],info[ch[u][]]));
}
经典的Splay翻转打标记函数reverse、单次下传函数pushdownOnce、一路下传函数pushdown、旋转函数rotate和伸展函数splay,没有什么特殊的地方:
void reverse(int u){
rev[u]^=;
swap(ch[u][],ch[u][]);
}
//为u打上翻转标记
void pushdownOnce(int u){
if(rev[u]){
if(ch[u][]) reverse(ch[u][]);
if(ch[u][]) reverse(ch[u][]);
rev[u]=;
}
}
//单次下传
void pushdown(int u){
if(!isroot(u)) pushdown(fa[u]);
pushdownOnce(u);
}
//从当前Splay的根节点一路下传到u,把一路的翻转都处理掉
void rotate(int u){
int f=fa[u],g=fa[f],c=who(u);
if(!isroot(f))
ch[g][who(f)]=u;
fa[u]=g;
ch[f][c]=ch[u][c^]; fa[ch[f][c]]=f;
ch[u][c^]=f; fa[f]=u;
update(f); update(u);
}
//将当前节点u旋转到父亲节点
void splay(int u){
pushdown(u);
while(!isroot(u)){
if(!isroot(fa[u]))
rotate(who(fa[u])==who(u)?fa[u]:u);
rotate(u);
}
}
//将u旋转到当前Splay的根节点
4. 关键函数:
$access(u)$,更改函数,把$u$到LCT根节点一路变成一条重链,同时断开一路上原来的重儿子:
void access(int u){
for(int v=;u;v=u,u=fa[u]){
splay(u);
ch[u][]=v;
update(u);
}
}
什么意思呢?外层for循环负责迭代从$u$一直到Splay根节点的路径,同时用$v$记录是从哪里来到$u$的。
每到达一个点$u$,我们将$u$提到树根,这时$u$的右儿子就是在原本重链上$u$的重儿子。我们把它替换成过来的节点,并更新信息即可。
$makeRoot(u)$,换根操作,使$u$成为树的根节点:
void makeRoot(int u){
access(u);
splay(u);
reverse(u);
}
换根换根,实际上影响到的是哪些因素呢?
仅仅是$u$到根节点一路上的Splay发生了父子反向,对于其它的Splay并没有影响。
于是这样调用:
- 我们把$u$到根节点一路变为重链,即把它们放到一棵Splay中;
- 将$u$旋转到Splay的根节点;
- 为$u$打上翻转标记。
这样就为$u$到根节点的信息完成了父子反向操作。
$link(a,b)$,连接操作,更改树形,连接a和b两个节点,即连接a和b所在的两棵LCT(前提是a和b不在同一棵LCT中):
void link(int a,int b){
makeRoot(a);
fa[a]=b;
}
我们将$a$变为$a$的LCT的根,然后将$a$的父亲设为$b$。这样就将$a$的整棵LCT连接到了$b$所在的LCT,并且$a$和$b$在定义上会相邻。
$cut(a,b)$,切割操作,更改树形,分离a和b两个节点,即分割出两棵独立的LCT(前提是a和b在同一棵LCT中且a和b相邻):
void cut(int a,int b){
makeRoot(a);
access(b);
splay(b);
fa[a]=;
ch[b][]=;
update(b);
}
我们将$a$变成树根,然后将$b$到树根(也就是$a$)一路变为重链,再将$b$旋转到所在Splay的根。
由于$a$和$b$同在一棵Splay中且$a$一定是$b$的父亲,所以Splay中$b$的左儿子一定是$a$,断开即可,记得更新,因为有了父子关系变化。
$isConnect(a,b)$,实现判断a和b是否在同一棵LCT中:
bool isConnect(int a,int b){
if(a==b) return true;
makeRoot(a);
access(b);
splay(b);
return fa[a];
}
我们将$a$变成树根,然后将$b$到树根(也就是$a$)一路变为重链,再将$b$旋转到所在Splay的根。
如果$a$和$b$不在同一棵LCT中,执行$makeRoot(a)$后,$a$的父亲应该为空($makeRoot$最后有一个$splay(u)$的操作将$u$旋转到树根)。
除非什么情况呢?除非a和b在同一棵LCT中,在$access(b)$并$splay(b)$后,$a$与$b$应该在同一棵Splay中,既然$b$为Splay根,那么$a$肯定不为Splay根,$a$一定有一个父亲存在。
至此,LCT的最常用函数已经介绍完毕,下面我们来总结一下最根本的核心思想:
可以发现$access(u)$和$splay(u)$总是配套出现,有时在前面配上$makeRoot$。这一套COMBO可以将$u$转到Splay树根,然后进行如同Splay一样的便捷操作。
比如想求$a$到$b$的点权之和,我们可以$makeRoot(a) + access(b) + splay(b)$,此时$a$和$b$一定在同一条重链、同一棵Splay中,然后我们统计Splay中$b$和$b$的左子树的点权之和就可以了。
总结
理解LCT以后就会觉得这玩意挺有意思的。一些处理信息、调用函数的思想,值得我们更多地推敲。
【Learning】一步步地解释Link-cut Tree的更多相关文章
- LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...
- Link Cut Tree 总结
Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...
- link cut tree 入门
鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...
- Codeforces Round #339 (Div. 2) A. Link/Cut Tree 水题
A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...
- Link/cut Tree
Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...
- 洛谷P3690 Link Cut Tree (模板)
Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...
- bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门
link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...
- P3690 【模板】Link Cut Tree (动态树)
P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...
- Link Cut Tree学习笔记
从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...
- [CodeForces - 614A] A - Link/Cut Tree
A - Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, ...
随机推荐
- 基于testng自动化添加allure报告展示以及jenkins集成
本地执行方式: 1.下载地址 http://allure.qatools.ru/ 2.执行机器添加环境变量 如mac:vi /etc/profile export ALLURE_HOME=/Users ...
- Mybatis JPA-集成方案+源码
2018-04-18 update 当前文章已过时,请访问代码仓库查看当前版本wiki. github https://github.com/cnsvili/mybatis-jpa gitee htt ...
- Linux☞如何修改文件权限
修改文件/目录的权限:chmod 规则 文件/目录名 规则: 角色:u 自己人 user g 同组人 group o 其他人 other a 所有人 all 操作: + - 权限 ...
- Codeforces Round #503 (by SIS, Div. 2) D. The hat
有图可以直观发现,如果一开始的pair(1,1+n/2)和pair(x, x+n/2)大小关系不同 那么中间必然存在一个答案 简单总结就是大小关系不同,中间就有答案 所以就可以使用二分 #includ ...
- Oracle同义词和序列
同义词:是表.索引.视图的模式对象的一个别名,通过模式对象创建同意词,可以隐藏对象的实际名称和 所有者信息,为对象提供一定的安全性,开发应用程序时:应该尽量避免直接使用表,视图 或其他对象,改用对象的 ...
- Pvmove中断后恢复LV状态
Pvmove中断后恢复LV状态 pvmove执行时关闭中断窗口后,pvmove进程会被强制杀掉,从而导致lv的状态异常,无法重新进行pvmove和其他lvm镜像增加相关操作,可以通过如下方式修复: ...
- app开发相关
app播放UIWebview 没有声音解决: 设置 allowsInlineMediaPlayback = YES; mediaPlaybackRequiresUserAction = NO
- 浅谈TSM概念、系统架构及技术发展
NFC作为一种近距离的无线通信技术,提供了一种更直接.更安全的现场交互解决方案.它能够允许电子设备之间进行非接触式点对点数据传输,实现数据交换.访问内容与服务.有了它,手机不再只是打电话.发短信以及上 ...
- "Hello World!"团队第四次会议
Scrum立会 博客内容是: 1.会议时间 2.会议成员 3.会议地点 4.会议内容 5.todo list 6.会议照片 7.燃尽图 一.会议时间: 2017年10月16日 11:44-12:18 ...
- 《Linux内核与分析》第七周
by 21035130王川东 Linux内核如何装载和启动一个可执行程序 一. EIF文件格式: 1.ELF头部在文件的开始,描述文件的总体格式,保存了路线图,描述该文件的组织情况,即生成该文件系统的 ...