BZOJ 2002 弹飞绵羊为例学习有根树LCT(Link-Cut Tree)


注:本文非常简单,只涉及有根树LCT,对于无根树,LCT还有几个本文没有提到的操作,以后慢慢更新 =v=

知识储备

  • [x] splay
  • [x] 树链剖分

题意

有一棵\(n\)个节点的有根树,动态修改父子关系(保证仍是一棵有根树),并询问某节点深度。

题解

这是一道LCT(Link-Cut Tree)的模板题。

Link-Cut Tree (又)是Tarjan发明的一种算法,可以解决一类动态树问题。“动态树问题”就是像本题这种,随时修改树的结构的问题。

LCT在思想上有些类似于树链剖分。

回顾一下树剖:

在树剖中,我们把边分为重边和轻边两种,重边相连形成了重链,而重链可以用线段树维护。

美中不足的是,线段树无法完成“动态修改”的需要。那么什么数据结构最灵活,能随时断开/连接呢?那当然是——splay啦。

所以,LCT就是用好多棵splay平衡树来维护原树上的每条“实链”(类似树剖的“重链”)。

具体是如何维护的呢?可以看这幅图:

左图是原树,右图是我们建出的splay树。

左图中的每一条实链都对应着右图中一棵“完整的”(不带“箭头”的)平衡树,如(1,2,3)、(4,5)、(6)各自组成一棵平衡树。每棵完整的平衡树都把节点在原树中的深度作为关键字,例如4比5深度小,所以是5的左儿子;3比1深度大,所以是1的右儿子。当然,身为splay,右图不是左图对应的唯一平衡树,还有很多种可能的合法平衡树。

而左图上的每一条虚边(u, v),则使用了一种“单向边”来维护:设u是父亲、v是儿子,则在右图中,v所在的平衡树的根节点“认”u所在的平衡树的根节点为父亲,而反过来,父亲却不认儿子。这种关系在右图中用单向的箭头表示了。例如,在原图中,(1, 4)是一条虚边,4所在的平衡树的根节点是5,1所在的平衡树的根节点是1,所以右图中fa[5] = 1,而1却不认为5是它的儿子。

好的,现在你对LCT如何实现有了个初步的印象了!那么接下来,我们逐个学习LCT涉及到的操作。

操作1:Access

Access操作是所有LCT操作的基础!Access(u)会把根节点到u路径上的所有边都变成“实边”,使得u和根节点处于一棵完整平衡树中。

代码实现:

void access(int u){
int v = 0; //一开始v是空节点,其余时刻v都是要与u连接的小平衡树的根节点
while(u){
splay(u); //将u在旋至它所在的平衡树的根节点
rs[u] = v; //因为v深度更大,所以让v作为u的右儿子
upt(u); //修改儿子后,莫忘调用单节点更新函数
v = u, u = fa[u];
}
}

是不是不算很难呐~

操作2:Cut

顾名思义,Cut就是切断原树中的一条边。下面代码中,Cut(u)表示切断u和父亲的连边。这道题是有根树,非常简单——先Access(u),使u和跟节点处在同一平衡树中,然后完全切断u向fa[u]连的那条边,即使得“父子互不相认”即可。

void cut(int u){
access(u);
splay(u);
fa[ls[u]] = 0, ls[u] = 0;
upt(u);
}

操作3:Link

顾名思义,Link就是绿帽子林克将两个节点连到一起,即在原树中加边的操作。

这道题是有根树,有根树中Link操作非常简单:Link(u, v)表示把u作为v的儿子——那么直接令fa[u] = v即可。

void link(int u, int v){
cut(u);void cut(int u){
access(u);
splay(u);
fa[ls[u]] = 0, ls[u] = 0;
upt(u);
}
fa[u] = v;
}

有根树LCT涉及的基本操作只有这三条!只要写个splay、再写这三个函数,BZOJ 2002 弹飞绵羊这道题就很好写啦!

附上我的代码(大部分是Copycat企鹅学长的……)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
#define enter putchar('\n')
#define space putchar(' ')
template <class T>
void read(T &x){
char c;
bool op = 0;
while(c = getchar(), c > '9' || c < '0')
if(c == '-') op = 1;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
}
const int N = 200005;
int n, m, ta, tb;
int fa[N], ls[N], rs[N], sze[N];
#define isroot(u) (ls[fa[u]] != (u) && rs[fa[u]] != (u))
#define which(u) (ls[fa[u]] == (u))
void upt(int u){
sze[u] = sze[ls[u]] + sze[rs[u]] + 1;
}
void rotate(int u){
int v = fa[u], w = fa[v], b = which(u) ? rs[u]: ls[u];
if(!isroot(v)) which(v) ? ls[w] = u: rs[w] = u;
which(u) ? (ls[v] = b, rs[u] = v): (rs[v] = b, ls[u] = v);
fa[u] = w, fa[v] = u;
if(b) fa[b] = v;
upt(v), upt(u);
}
void splay(int u){
while(!isroot(u)){
if(!isroot(fa[u])){
if(which(u) == which(fa[u])) rotate(fa[u]);
else rotate(u);
}
rotate(u);
}
}
void access(int u){
int v = 0;
while(u){
splay(u);
rs[u] = v;
upt(u);
v = u, u = fa[u];
}
}
void cut(int u){
access(u);
splay(u);
fa[ls[u]] = 0, ls[u] = 0;
upt(u);
}
void link(int u, int v){
cut(u);
fa[u] = v;
}
int main(){
read(n);
for(int i = 1; i <= n; i++)
read(ta), sze[i] = 1, fa[i] = ta + i > n ? 0 : ta + i;
read(m);
while(m--){
read(ta);
if(ta == 1){
read(ta), ta++;
access(ta);
splay(ta);h
write(sze[ls[ta]] + 1), enter;
}
else{
read(ta), read(tb), ta++;
link(ta, ta + tb > n ? 0 : ta + tb);
}
}
return 0;
}

以 BZOJ 2002 为例学习有根树LCT(Link-Cut Tree)的更多相关文章

  1. 学习笔记:Link Cut Tree

    模板题 原理 类似树链剖分对重儿子/长儿子剖分,Link Cut Tree 也做的是类似的链剖分. 每个节点选出 \(0 / 1\) 个儿子作为实儿子,剩下是虚儿子.对应的边是实边/虚边,虚实时可以进 ...

  2. LCT(link cut tree) 动态树

    模板参考:https://blog.csdn.net/saramanda/article/details/55253627 综合各位大大博客后整理的模板: #include<iostream&g ...

  3. 【学习笔记】LCT link cut tree

    大概就是供自己复习的吧 1. 细节讲解 安利两篇blog: Menci 非常好的讲解与题单 2.模板 把 $ rev $ 和 $ pushdown $ 的位置记清 #define lc son[x][ ...

  4. [BZOJ 2002] [HNOI2010]弹飞绵羊(Link Cut Tree)

    [BZOJ 2002] [HNOI2010]弹飞绵羊(Link Cut Tree) 题面 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一 ...

  5. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  6. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  7. 洛谷P3690 [模板] Link Cut Tree [LCT]

    题目传送门 Link Cut Tree 题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两个整数(x,y),代 ...

  8. LuoguP3690 【模板】Link Cut Tree (动态树) LCT模板

    P3690 [模板]Link Cut Tree (动态树) 题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两 ...

  9. BZOJ 3282 Link Cut Tree (LCT)

    题目大意:维护一个森林,支持边的断,连,修改某个点的权值,求树链所有点点权的异或和 洛谷P3690传送门 搞了一个下午终于明白了LCT的原理 #include <cstdio> #incl ...

随机推荐

  1. 大数据入门第二十四天——SparkStreaming(二)与flume、kafka整合

    前一篇中数据源采用的是从一个socket中拿数据,有点属于“旁门左道”,正经的是从kafka等消息队列中拿数据! 主要支持的source,由官网得知如下: 获取数据的形式包括推送push和拉取pull ...

  2. 网络对抗技术 2017-2018-2 20152515 Exp5 MSF基础应用

    1.实践内容(3.5分) 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路. 1.1一个主动攻击实践,如ms08_067; (1分) MS08-067漏洞攻击 这次使用 ...

  3. 【php增删改查实例】第四节 -自己 DIY 一个数据库管理工具

    本节介绍如何自己DIY一个数据库管理工具,可以在页面输入sql 进行简单的增删改查操作. 首先,找到xampp的安装目录,打开htdocs: 新建一个php文件,名称为 mysqladmin.php ...

  4. 【转载】C++引用详解

    原文:http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直 ...

  5. 洛咕 P4474 王者之剑

    宝石只能在偶数秒取到,假设有一个宝石在奇数秒取到了,那么上一秒是偶数秒,在上一秒的时候这里的宝石就没了. 相邻的两个宝石不能同时取,很显然,先取一块,那么这是偶数秒,取完了这一块之后相邻的都没了. 只 ...

  6. Java设计模式-建造者(Builder)模式

    目录 由来 使用 1. 定义抽象 Builder 2. 定义具体 Builder类 3. 定义具体 Director类 4. 测试 定义 文字定义 结构图 优点 举例 @ 最近在看Mybatis的源码 ...

  7. 用C语言操作MySQL数据库,进行连接、插入、修改、删除等操作

    C/C++ code   ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 3 ...

  8. Jmeter(八)-发送JDBC请求

    下午花了两个小时研究了一下Jmeter发送JDBC请求,现在把基本操作流程分享一下. 做JDBC请求,首先需要两个jar包:mysql驱动-mysql-connector-java-5.1.13-bi ...

  9. 部署jar项目常用命令

      netstat -tunlp | grep  ××   查询出端口为××在运行应用的线程ip   kill -9  ××     关闭线程ip 为 ××的应用   rm  -f  ××.jar  ...

  10. BugPhobia开发篇章:Beta阶段第II次Scrum Meeting

    0x01 :Scrum Meeting基本摘要 Beta阶段第二次Scrum Meeting 敏捷开发起始时间 2015/12/13 00:00 A.M. 敏捷开发终止时间 2015/12/14 22 ...