LinkCutTree学习笔记
LinkCutTree 学习笔记
参考来源
https://www.zybuluo.com/xzyxzy/note/1027479
https://www.cnblogs.com/zhoushuyu/p/8137553.html
目的&作用
树的动态加边/删边
维护两点联通性, 树链信息, 生成树等
概述
应为无根树可以随意钦点树根, Splay 树也同样
所以下面的算法都基于从根连出去的树链形成的 Splay 树, 以及换根, 等等操作
然后实际上并不用建出原树, 所有的连边的关系都在 LCT 中
详解
Splay以什么为关键值?以什么为下标?
以深度为关键值(不用具体存储, 用二叉查找树表示相对关系)
以原树中编号为下标
实边和虚边
- 每一条由实边形成的树链由一个 Splay 维护
- 每个点至多有一条连向其儿子的实边
- 一个原树会被划分为多块 Splay
父与子
假设 ch[x][0/1] 代表在 Splay 中 x 节点的左右儿子, fa[x] 为父亲
- 在原树中与
x虚边连接的节点就不会出现在 Splay 树的父子中 - 一个 Splay 的
root的fa连向原树中该实链链顶的父亲(特例), 而父亲没有这个儿子(显然)
isroot(x)
判断 x 是否是它所在的 Splay 的根
判据很简单, 就是 "Splay 上父亲的左右儿子都不是它", 刚才讲过了
注意是左右儿子都不是, 因为以深度为下标, 而深度时常会改变
bool isroot(int x)
{
return ch[fa[x]][0] != x && ch[fa[x]][1] != x;
}
这样就不记录 root 了
正是因为 root 会连接两个不同的 Splay , 所以在\(LCT\)中要特别注意边界问题, 在rotate()和splay()等中都会涉及
access(x)
将树根到 x 的路径串在一个 Splay 中, 并修改相关实边虚边
由于 Splay 的根是连向其他 Splay 的出口, 而实链链顶的父亲(\(fa[root]\))则是另一个 Splay 的入口, 我们主要关注这种节点, 还有 x 点
- 发现将
root和fa[root]改实边后, \(fa[root]\)的实边就要断开, 而该实儿子是\(ch[][1]\)(深度较大) - 切换虚实边只要
ch[fa[root]][1]=root即可, 一步到位
我们能保证 root 刚被包含在根到 x 的路径上, 即新的 Splay 分区中(见makeroot), (但是为了保证复杂度?), 一开始将 x splay到根, 之后则选择\(fa[root]\), 即 fa[x] splay到根
还有, splay改变了树的形态, 而splay的路径上的点可以在rotate的时候pushup掉, 那么还剩下根刚好没有pushup, 单独做一下即可(pushup(fa[x])时会把 x 统计上去, 不用担心新实边上的上传)
总结一下代码就是这样
注意一开始 x 的右儿子会连接 0 , 就是把 x 和比 x 深的那一部分断开了, 分了一个 Splay 出去, 但并不影响
然后做完之后 x 在最底下
void access(int x)
{
for (int y = 0; x; y = x, x = fa[x])
splay(x), ch[x][1] = y, pushup(x);
}
"access"好难打啊,不如直接留一个头尾
基于access()的一些操作
makeroot()
原树换根,用处看下面一些函数就知道了
先 access(x)
发现现在已经把\(root --> x\)抓起来了,而 x 在最深处
于是splay(x) + reverse(x)就可以了(想想为什么?)
因为只需要这条链翻转, 而链上挂出去的小树不用
void makeroot(int x)
{
access(x); splay(x);
reverse(x);
}
findroot()
找 splay 上的根
看代码
int findroot(int x)
{
access(x); splay(x);
while (ch[x][0]) x = ch[x][0];
splay(x);
return x;
}
link(x, y)
void link(int x, int y)
{
if (findroot(x) == findroot(y)) return ;
makeroot(x);
fa[x] = y; //x整棵树接到y下面
}
split(x, y)和cut(x, y)
可以随便换根的
看代码,想想为什么?
void split(int x, int y)
{
makeroot(x);
access(y); splay(y);
}
/*判断
void cut(int x, int y)
{
makeroot(x);
if (findroot(y) != x || fa[y] != x) return ; // 想想为什么?
split(x, y);
fa[x] = ch[y][0] = 0;
pushup(y);
}
*/
void cut(int x, int y)
{
split(x, y);
fa[x] = ch[y][0] = 0;
pushup(y);
}
reverse(x)
void reverse(int now)
{
if (!now) return ;
swap(ch[now][0], ch[now][1]);
rev[now] ^= 1;
}
rotate()和splay(x)
将splay的路径上从上到下先pushdown()
void rotate(int x)
{
int y = fa[x], z = fa[y];
int yx = (ch[y][1] == x), zy = (ch[z][1] == y);
if (!isroot(y)) ch[z][zy] = x;
fa[x] = z;
fa[ch[x][yx ^ 1]] = y; ch[y][yx] = ch[x][yx ^ 1];
fa[y] = x; ch[x][yx ^ 1] = y;
pushup(y); pushup(x);
}
void splay(int x)
{
if (!x) return ;
stk[top = 1] = x;
for (int p = x; !isroot(p); p = fa[p]) stk[++ top] = fa[p];
while (top) pushdown(stk[top --]);
while (!isroot(x))
{
int y = fa[x], z = fa[y];
int zy = (ch[z][1] == y), yx = (ch[y][1] == x);
if (!isroot(y))
(zy == yx) ? rotate(y) : rotate(x);
rotate(x);
}
}
后续见例题,基础代码见模板题
- 带修改判断两点联通性
- 带修改查询路径异或和
例题
BZOJ2049: Sdoi2008Cave 洞穴勘测
树, 三种操作, 连边, 删边(保证存在), 查询连通性
模板Link Cut Tree(动态树)
树, 四种操作, 查询路径上异或和, 连边(要求已联通则不连), 删边(不保证联通), 修改点权
修改点权:把根换成他,这样单点修改他就行了,影响之后要用的时候自然会算到
查询:split出来,完事
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 3e5 + 10;
inline LL in()
{
LL x = 0, flag = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') flag = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x * flag;
}
int n, m;
int fa[MAXN], ch[MAXN][2], rev[MAXN];
LL val[MAXN], sum[MAXN];
int top, stk[MAXN];
void reverse(int x)
{
if (!x) return;
swap(ch[x][0], ch[x][1]);
rev[x] ^= 1;
}
void pushup(int x)
{
sum[x] = sum[ch[x][0]] ^ sum[ch[x][1]] ^ val[x];
}
void pushdown(int x)
{
if (!rev[x]) return;
reverse(ch[x][0]); reverse(ch[x][1]);
rev[x] = 0;
}
bool isroot(int x) { return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }
void rotate(int x)
{
int y = fa[x], z = fa[y];
int zy = (y == ch[z][1]), yx = (x == ch[y][1]);
if (!isroot(y)) ch[z][zy] = x;
fa[x] = z; // fa
ch[y][yx] = ch[x][yx ^ 1], fa[ch[x][yx ^ 1]] = y;
ch[x][yx ^ 1] = y; fa[y] = x;
pushup(y); pushup(x);
}
void splay(int x) // x --> root
{
if (!x) return ;
stk[top = 1] = x;
for (int tmp = x; !isroot(tmp); tmp = fa[tmp]) stk[++ top] = fa[tmp];
while (top) pushdown(stk[top --]);
while (!isroot(x))
{
int y = fa[x], z = fa[y];
int zy = (y == ch[z][1]), yx = (x == ch[y][1]);
if (!isroot(y))
(zy == yx) ? rotate(y) : rotate(x);
rotate(x);
}
pushup(x);
}
void access(int x)
{
for (int y = 0; x; y = x, x = fa[x])
splay(x), ch[x][1] = y, pushup(x);
}
void makeroot(int x)
{
access(x); splay(x);
reverse(x);
}
int findroot(int x)
{
access(x); splay(x);
while (ch[x][0]) x = ch[x][0];
splay(x);
return x;
}
void link(int x, int y)
{
if (findroot(x) == findroot(y)) return ;
makeroot(x);
fa[x] = y;
}
void split(int x, int y)
{
makeroot(x);
access(y); splay(y);
}
void cut(int x, int y)
{
makeroot(x);
if (findroot(y) != x || fa[y] != x) return ;
split(x, y);
fa[x] = ch[y][0] = 0;
pushup(y);
}
LL query(int x, int y)
{
split(x, y);
return sum[y];
}
void modify(int x, LL y)
{
access(x); splay(x);
val[x] = y;
pushup(x);
}
int main()
{
n = in(), m = in();
for (int i = 1; i <= n; ++ i) val[i] = in();
while (m --)
{
int opt = in(), x = in(); LL y = in();
if (opt == 0) printf("%lld\n", query(x, y));
else if (opt == 1) link(x, y);
else if (opt == 2) cut(x, y);
else if (opt == 3) modify(x, y);
}
return 0;
}
WC2006水管局长
https://www.lydsy.com/JudgeOnline/problem.php?id=2594
https://www.luogu.org/problemnew/show/P4172
BZOJ3626: LNOI2014LCA
BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊
裸题, 根据题意建立虚拟点, 删边加边,
询问是原树中以某个点为根的某个点的深度
可以先 makeroot 根, 然后 access, splay x , 直接用 siz 求得,
也可你当成求路径长度
再放一个板子
namespace LCT
{
int ch[MAXN][2], fa[MAXN];
int siz[MAXN];
bool rev[MAXN];
int stk[MAXN], top;
void init(int x)
{
siz[x] = 1;
ch[x][0] = ch[x][1] = fa[x] = 0;
}
bool isroot(int x)
{
return ch[fa[x]][1] != x && ch[fa[x]][0] != x;
}
void pushup(int now)
{
siz[now] = siz[ch[now][0]] + siz[ch[now][1]] + 1;
}
void reverse(int now)
{
if (!now) return ;
swap(ch[now][0], ch[now][1]);
rev[now] ^= 1;
}
void pushdown(int now)
{
if (!rev[now]) return ;
reverse(ch[now][0]); reverse(ch[now][1]);
rev[now] = 0;
}
void rotate(int x)
{
int y = fa[x], z = fa[y];
int yx = (ch[y][1] == x), zy = (ch[z][1] == y);
if (!isroot(y)) ch[z][zy] = x;
fa[x] = z;
fa[ch[x][yx ^ 1]] = y; ch[y][yx] = ch[x][yx ^ 1];
fa[y] = x; ch[x][yx ^ 1] = y;
pushup(y); pushup(x);
}
void splay(int x)
{
if (!x) return ;
stk[top = 1] = x;
for (int p = x; !isroot(p); p = fa[p]) stk[++ top] = fa[p];
while (top) pushdown(stk[top --]);
while (!isroot(x))
{
int y = fa[x], z = fa[y];
int zy = (ch[z][1] == y), yx = (ch[y][1] == x);
if (!isroot(y))
(zy == yx) ? rotate(y) : rotate(x);
rotate(x);
}
}
void access(int x)
{
for (int pre = 0; x != 0; pre = x, x = fa[x])
splay(x), ch[x][1] = pre, pushup(x);
}
void makeroot(int x)
{
access(x); splay(x); reverse(x);
}
int findroot(int x)
{
access(x); splay(x);
while (ch[x][0]) x = ch[x][0];
splay(x);
return x;
}
void link(int x, int y)
{
if (findroot(x) == findroot(y)) return ;
makeroot(x);
fa[x] = y;
}
void split(int x, int y)
{
makeroot(x);
access(y); splay(y);
}
void cut(int x, int y)
{
split(x, y);
fa[x] = ch[y][0] = 0;
pushup(y);
}
int getdep(int x)
{
makeroot(n + 1);
access(x);
splay(x);
return siz[ch[x][0]] + 1;
}
}
int main()
{
n = in();
for (int i = 1; i <= n + 1; ++ i)
{
LCT::init(i);
}
for (int i = 1; i <= n; ++ i)
{
a[i] = in();
LCT::link(i, min(i + a[i], n + 1));
}
m = in();
while (m --)
{
int opt = in();
if (opt == 1)
{
int x = in() + 1;
printf("%d\n", LCT::getdep(x) - 1);
}
else
{
int x = in() + 1, v = in();
// (x --> x + a[x]) ==> (x --> x + v)
LCT::cut(x, min(x + a[x], n + 1));
LCT::link(x, min(x + v, n + 1));
a[x] = v;
}
}
return 0;
}
LinkCutTree学习笔记的更多相关文章
- LCT[Link-Cut-Tree学习笔记]
部分摘抄于 FlashHu candy99 所以文章篇幅较长 请有足够的耐心(不是 其实不用学好splay再学LCT的-/kk (至少现在我平衡树靠fhq) 如果学splay的话- 也许我菜吧-LCT ...
- [学习笔记]平衡树(Splay)——旋转的灵魂舞蹈家
1.简介 首先要知道什么是二叉查找树. 这是一棵二叉树,每个节点最多有一个左儿子,一个右儿子. 它能支持查找功能. 具体来说,每个儿子有一个权值,保证一个节点的左儿子权值小于这个节点,右儿子权值大于这 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- seaJs学习笔记2 – seaJs组建库的使用
原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...
- CSS学习笔记
CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...
随机推荐
- 职位画像分析(pandas/ matplotlib)
一.数据分析的步骤 二.提出问题 (1) 分析数据分析师主要的技能排名? (2) 分析数据分析师薪资和岗位地点.学历.工作年限的关系? (3) 数据分析师的学历需求? (4) 不同城市数据分析师的需求 ...
- HDUNumber Sequence(KMP)
传送门 题目大意:b在a第一次出现的位置 题解:KMP 代码: #include<iostream> #include<cstdio> #include<cstring& ...
- vscode源码分析【二】程序的启动逻辑,第一个窗口是如何创建的
上一篇文章:https://www.cnblogs.com/liulun/ (小广告:我做的开源免费的,个人知识管理及自媒体营销工具“想学吗”:https://github.com/xland/xia ...
- sqoop的详细使用及原理
转自:https://blog.csdn.net/zhusiqing6/article/details/95680185 1.sqoop简介sqoop是一个用来将hadoop中hdfs和关系型数据库中 ...
- python学习(day1-2)
python 学习(day - 1-2 ) 变量:将运算的中间结果暂存到内存,以便后续程序调⽤. 变量的命名规则: 1, 变量由字⺟, 数字,下划线搭配组合⽽成 2, 不可以⽤数字开头,更不能是全数字 ...
- vue 渐变 进度条 progress
废话 不多少说 ,直接上代码 新建文件 gradual-progress.vue <!-- * @Author: gfc * @Date: 2019-11-07 14:00:11 * @Last ...
- 使用JaCoCo Maven插件创建代码覆盖率报告
这篇博客文章描述了我们如何使用JaCoCo Maven插件为单元和集成测试创建代码覆盖率报告. 我们的构建要求如下: 运行测试时,我们的构建必须为单元测试和集成测试创建代码覆盖率报告. 代码覆盖率报告 ...
- Windows 10 powershell 中文乱码解决方案
Windows 10 powershell 中文乱码解决方案 Intro 我装的系统是英文版的 win 10 操作系统,最近使用命令行测试接口,发现中文显示一直异常, 使用网上的各种解决方案都没有效果 ...
- 数据库之MySQL查询
查询 创建数据库.数据表 -- 创建数据库 create database python_test1 charset=utf8; -- 使用数据库 use python_test1; -- stude ...
- Spring Security 教程 大牛的教程
https://www.iteye.com/blog/elim-2247073 Spring Security 教程 Spring Security(20)——整合Cas Spring Securit ...