[算法模版]Link-Cut-Tree
[算法模版]Link-Cut-Tree
博主懒本博客只对现有博客进行补充,先直接放隔壁链接。
基本思想
即“使用一个splay维护一条链,且这条链不存在在原树上的深度相同的点”。而这条链在splay中的存储方式是以节点在原树上的深度为关键字进行排序。(这也意味着这个splay的中序遍历和这条链在原树上的中序遍历是相同的,且这个中序遍历就是把这个链从浅到深写下来)
在push_up
操作中这是个关键的性质。因为splay上的节点x
的左子树就是他的一部分祖先节点,右子树就是他的一部分儿子节点。
makeroot操作
makeroot操作用于把任何一个点反转到当前树的根节点。
做法是先把要进行操作的节点x进行access,将root和x进行连通。然后进行splay(x)操作,把x变成splay的根。(请注意,这时候x在主树的深度仍然没有改变)。
随后将x的子树全部进行反转操作。也就是改变了这个splay的深度。虽然splay和splay之间的连接需要依赖深度关系(一棵splay的虚边连接向当前splay中序遍历序列的首位在原树上的父亲)。但是因为相对关系不变,所以不影响。
看图(图来自 动态仙人掌系列题解之四):
可以把连接老根和新根的splay看作一个无法弯曲的杆子,其他splay都是连接在杆子上的块。旋转操作虽然会改变杆子上每一点的深度,但是却不会改变块和杆子上连接点的相对深度关系。所以不会这样变换老根和新根不会对树的结构造成破坏。
另外如果维护的值是和树的形态相关的,这样使用makeroot
很可能就会出锅(比如 SP2939 QTREE5 - Query on a tree V)。那么makeroott
就不能使用了。因为涉及makeroot
的操作有link,cut,split
。我们得想办法解决这个问题:
Link
之所以需要在link
中makeroot(x)
,是因为根据定义,对于一条虚边u,v
,在原树上是一条连接u,root[v]
的边(这里root[]
是splay的root)。所以我们需要保证连接的其中一个端点f[x]=0且root[x]=x
即可。
findroot操作
因为已经makeroot(x)
了,所以x
一定是最浅的点。我们必须要保证三个条件才能cut
:
- 在同一棵树。
y
的父亲是x
。y
没有左子树。
后两个条件保证了x
和y
在原树上是直接连接的。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn (int)(3e5+1000)
int f[maxn],v[maxn],s[maxn],st[maxn],c[maxn][2];
bool r[maxn];
using namespace std;
int n,m;
void pushr(int x){
swap(c[x][0],c[x][1]);r[x]^=1;
}
void pushdown(int x){
if(!r[x])return;
if(c[x][0])pushr(c[x][0]);
if(c[x][1])pushr(c[x][1]);
r[x]=0;
}
void pushup(int x){
s[x]=s[c[x][0]]^s[c[x][1]]^v[x];
}
bool nroot(int x){
return c[f[x]][0]==x||c[f[x]][1]==x;
}
void rotate(int x){
int y=f[x],z=f[y],k=(c[y][1]==x),w=c[x][!k];
bool flag=nroot(y);
c[y][k]=c[x][!k];
f[c[x][!k]]=y;
c[x][!k]=y;
f[y]=x;
if(flag)c[z][c[z][1]==y]=x;
f[x]=z;
pushup(y);
pushup(x);
}
void splay(int x){
int y=x,z=0;
st[++z]=y;
while(nroot(y))st[++z]=y=f[y];
while(z)pushdown(st[z--]);
for(;nroot(x);rotate(x)){
y=f[x];
if(!nroot(f[x]))continue;
rotate((c[f[x]][0]==x)==(c[f[y]][0]==y)?y:x);
}
// pushup(x);
}
void access(int x){
for(int y=0;x;y=x,x=f[x]){
splay(x);c[x][1]=y;pushup(x);
}
}
int findroot(int x){
access(x);splay(x);
while(c[x][0]){
x=c[x][0];
pushdown(x);
}
splay(x);
return x;
}
void makeroot(int x){
access(x);splay(x);pushr(x);
}
void split(int x,int y){
makeroot(x);access(y);splay(y);
}
void link(int x,int y){
if(findroot(x)!=findroot(y)){makeroot(x);f[x]=y;}
}
void cut(int x,int y){
makeroot(x);
if(findroot(y)==x&&f[y]==x&&!c[y][0]){
f[y]=c[x][1]=0;
pushup(x);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&v[i]);
for(int i=1;i<=m;i++){
int ty,x,y;scanf("%d%d%d",&ty,&x,&y);
if(ty==0){
split(x,y);
printf("%d\n",s[y]);
}
else if(ty==1){
link(x,y);
}
else if(ty==2){
cut(x,y);
}
else if(ty==3){
splay(x);v[x]=y;//pushup(x);
}
}
}
易错点
- 调用
access(x)
后,x
所在splay的根可能不是x
,也可能不是原树的根。所以在查询某条路径\(u,v\)的答案时,一定需要在makeroot(x),access(y)
之后必须加上splay(x)
或splay(y)
,才能保证x或y在splay的根上。 findroot
时应该先pushdown
再判断左儿子是不是0,然后才能向左儿子走(因为可能左儿子原来不是0,pushdown
之后变成了0,这时候走左儿子就会炸掉)。所以应该写成:while(c[x][0])x=c[x][0],push_down(x);
cut
中判断条件合法会进行findroot(y)
,但是findroot(y)
末尾如果没有对查询结果执行splay()
就会锅掉。因为cut
中会先makeroot(x)
,findroot(y)
如果查询之后不把结果splay
上去那么根就不是x
了,接着cut
就会出锅。
[算法模版]Link-Cut-Tree的更多相关文章
- 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 ...
- LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...
- 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, ...
- Link Cut Tree 总结
Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...
随机推荐
- golang --os系统包详解
环境变量 Environ 获取所有环境变量, 返回变量列表 func Environ() []string package main import ( "fmt" "os ...
- 【Elasticsearch】【WEB】java web服务连接es elasticsearch始终报错,无法正常连接使用的错误解决历程
前情提要: web服务往华为云上迁移 ================内网的好环境,相关配置=================== 1.web服务关于ES的集群配置如下: elasticAddress ...
- 【JVM】jmap命令详解----查看JVM内存使用详情
linux获取java进程PID: https://www.cnblogs.com/sxdcgaq8080/p/10734752.html 如果命令使用过程中报错,可能解决你问题的方案: https: ...
- Java I/O系统学习系列三:I/O流的典型使用方式
尽管可以通过不同的方式组合IO流类,但我们可能也就只用到其中的几种组合.下面的例子可以作为典型的IO用法的基本参考.在这些示例中,异常处理都被简化为将异常传递给控制台,但是这只有在小型示例和工具中才适 ...
- 开发--Deepin系统安装
开发|Deepin系统安装 在18小时前,我刚刚萌生了一个将我的笔记本换成linux系统.在18小时后的现在,在我各种试错之后,笔记本已经开始跑起linux了.在科技的时代,只要是想法,都可以试一试. ...
- 【开发笔记】- MySQL中limit查询超级慢,怎么办?
有如下解决方法: (1).通过判断id的范围来分页 limit ; 也得到了分页的数据,但是我们发现如果id不是顺序的,也就是如果有数据删除过的话,那么这样分页数据就会不正确,这个是有缺陷的. (2) ...
- ES6迭代器
说起迭代器, 那就要先了解迭代模式 迭代模式: 提供一种方法可以顺序获得聚合对象中的各个元素, 是一种最简单, 也是最常见的设计模式,它可以让用户通过特定的接口寻访集合中的每一个元素 而不用了解底层的 ...
- 4 Linux文件与目录管理
Linux的目录文件结构为树状结构,最顶级目录为根目录 / 其他目录通过挂载可以将他们添加到树中,通过解除挂载可以移除他们 绝对路径: 根目录开始 eg: /usr/share/doc 相对路径: 不 ...
- CDA数据分析【数据收集】
一.机器收集数据 机器收集数据会从不同角度对数据进行抓取和采集,与之前手动收集数据不同,机器收集数据不再是用小样本.特定样本来采集和分析整体数据,而是采用大样本或整体数据进行分析,这打破了原来的数据分 ...
- 用matlab计算线性回归问题
看机器学习的时候遇到的第一个算法就是线性回归,高数中很详细的说明了线性回归的原理和最小2乘法的计算过程,很显然不适合手动计算,好在各种语言都有现成的函数使用,让我们愉快的做个调包侠吧 简单线性回归 R ...