Link-Cut-Tree(动态树 LCT)


1.定义

1. 偏爱子节点: 一颗子树内最后访问的点若在该子树根节点 X 的某个儿子节点 P 的子树中,则称 P 为 X 的偏爱子节点。

2. 偏爱边:连向偏爱子节点的边。

3. 偏爱路径:一条全为偏爱边构成的路径(一定是一条链,类似于树链剖分的重链)

4. 每一条偏爱路径(以下直接写做重链)都由一棵Splay来进行维护.

注意:

  1. LCT不一定是二叉树。
  2. 若 p->fa==NULL 则p为其所在 LCT 的树根

    3. Splay -> 路径(且一定是树上的一条链)

    4. LCT -> 一棵树(由多棵Splay构成的森林)

    5. 整张图 -> 多棵 LCT 构成的森林(好几片Splay构成的森林)

    6. Splay作为辅助树,改变的只是 LCT 中的轻重边关系

    7. 基本的结构体变量
struct node{
node* fa;node* son[2];
bool rev,is_root;//区间反转标记和是否是Splay的根
}

2.基本操作

代码均使用指针。

可自己画图以理解。

注:以下代码中有:

#define __ NULL
#define ls son[0]
#define rs son[1]
#define get_son(a) (a->fa->rs==a)
  1. Splay的操作(为了适应LCT有些改动)
void rotate(node* p)
{
if(p->is_root) return;
register node* q=p->fa;register node* gp=q->fa;
register int k=get_son(p);
q->son[k]=p->son[k^1];
if(p->son[k^1]!=__) p->son[k^1]->fa=q;
p->fa=gp;
if(q->is_root) q->is_root=0,p->is_root=1;
//由于不能影响到另一棵Splay,要用 is_root 判断
else if(gp!=__) gp->son[get_son(q)]=p;
q->fa=p;p->son[k^1]=q;//记住旋转时指针指向的变更顺序!!
return;
}
void Splay(node* p)
{
if(p==__) return;
push(p);//在Splay到根之前要先下传标记(区间反转)
while(!p->is_root){//别旋出去啦!
if(p->fa->is_root) {rotate(p);break;}
if(get_son(p)==get_son(p->fa)) rotate(p->fa);
else rotate(p);rotate(p);
}
return ;
}

2.\(access(p)\) 访问某节点

void access(node* p)//访问某节点 p ,单独开辟一条路到节点 p ;
{
register node* pre=__;
while(p!=__){
Splay(p);
if(p->rs!=__) p->rs->is_root=1;
p->rs=pre;//切断右子树(不同Splay的父亲记录只从子到父)
if(pre!=__) pre->is_root=0;
//pre一定是上一棵Splay的根,要把它加到另一棵Splay中;
//updata(p);(更新)
pre=p;p=p->fa;
}
}

3.\(makeroot(p)\) 使p变为其所在LCT的根节点

void m_root(node* p)//使某个点成为根
{
if(p==__) return;
access(p);Splay(p);//连一条从树根到 p 的路径
p->rev^=1;
/*
因为树根的深度应要是最小的,上面操作只是让 p 成为了原树根所在Splay的根,
但他仅有左子树,深度最大,故还应该反转该Splay,使得 p 成为真正的根
(即保证深度为整棵LCT中深度最小的点); 上述操作并不会改变原树的结构
*/
}

4.\(Link(u,v)\) 连接u,v所在的LCT,使之变为一棵LCT

void Link(node* p,node* q)
{
m_root(p);//不改变p所在树的结构,只是将p提取出来便于操作
p->fa=q;//连接了两棵LCT改变了原图的结构(连的边是轻边,不用updata)
}

5.\(split(u,v)\) 分离出u->v这条路径,其上的点都在一颗Splay中,可迅速获得路径上的信息

void split(node* p,node* q){
m_ root(p);
access(q);
Splay(q);
}

6.\(Cut(u,v)\)砍断边\((u,v)\)分成两棵LCT

void Cut(node* p,node* q)
{
split(p,q);
if(q->ls==p) p->fa=q->ls=__,p->is_root=1;
//此时q所在Splay仅有他一个节点
}

6.标记的下放

void push_down(node* p)
{
if(p==__||p->rev==0) return;
p->rev=0;
if(p->ls!=__) p->ls->rev^=1;if(p->rs!=__) p->rs->rev^=1;
swap(p->ls,p->rs);
}
void push(node* p){//数据不大时可写成递归版本
h=1;st[h]=p;
while(!p->is_root) st[++h]=(p=p->fa);
for(int i=h;i;i--) push_down(st[i]);
}
void push(node* p)//递归版本
{
if(p==__) return;
if(p->is_root==0) push(p->fa);
push_down(p);
}

7.\(find(p)\) 返回p所在LCT的根节点(通常用于判断两点是否联通)

node* find(node* p)
{
for(access(p),Splay(p);p->ls!=__;p=p->ls);
return p;
}

3.LCT的应用

LCT可以很方便的提取出两点之间的路径和改变原树的形态,因此在处理一些树上的路径问题非常行之有效。

1.动态询问两点之间的连通性

例题.LuoguP2147 [SDOI2008]Cave 洞穴勘测 用find操作即可

2.动态询问树上两点间的距离

例题1.BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊

题解

例题2.LuoguP3721 [AH2017/HNOI2017]单旋

题解

3.各种树上路径问题

例题1.BZOJ3669: [Noi2014]魔法森林 动态维护最小生成树

题解

持续更新...

动态树(Link-Cut-Tree)简单总结(指针版本)的更多相关文章

  1. 动态树(Link Cut Tree) :SPOJ 375 Query on a tree

    QTREE - Query on a tree #number-theory You are given a tree (an acyclic undirected connected graph) ...

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

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

  3. P3690 【模板】Link Cut Tree (动态树)

    P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...

  4. 【刷题】洛谷 P3690 【模板】Link Cut Tree (动态树)

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

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

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

  6. LG3690 【模板】Link Cut Tree (动态树)

    题意 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y是联通的 ...

  7. link cut tree 入门

    鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构. link cut tree:研究popoqqq那个神ppt. bzoj1036:维护access操作就可以了. ...

  8. Link Cut Tree学习笔记

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

  9. [BJOI2014]大融合(Link Cut Tree)

    [BJOI2014]大融合(Link Cut Tree) 题面 给出一棵树,动态加边,动态查询通过每条边的简单路径数量. 分析 通过每条边的简单路径数量显然等于边两侧节点x,y子树大小的乘积. 我们知 ...

  10. 学习笔记:Link Cut Tree

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

随机推荐

  1. C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)

    委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...

  2. PHP 按照时区获取当前时间

    /**  * 时间格式化  * @param string $dateformat 时间格式  * @param int $timestamp 时间戳  * @param int $timeoffse ...

  3. switch-case分支结构总结

    1,格式 switch(表达式){ case 常量1:执行语句1: case 常量1:执行语句1: ... ... case 常量n:执行语句n: default:执行语句:} 2,说明: 根据swi ...

  4. Android Studio 如何获取 text文本内容

    1.找到目录的main先建立assets格式的文件夹 2.再把需要读取的txt 文件放入到该文件夹下(名字随意),这里取 list.txt. 文件内容 格式如下 3.读取文本内容 工具代码 /** * ...

  5. Java计算两个时间的天数差与月数差 LocalDateTime

    /**  * 计算两个时间点的天数差  * @param dt1 第一个时间点  * @param dt2 第二个时间点  * @return int,即要计算的天数差  */ public stat ...

  6. 洛谷 P1879 玉米田Corn Fields 题解

    题面 一道思维难度不大的状态压缩,也并不卡常,但细节处理要格外注意: f[i][j]表示前i行最后一行状态是j的方案数 #include <bits/stdc++.h> #define p ...

  7. PythonDay15

    第十五章装饰器_递归 今日内容 带参数装饰器 多个装饰器修饰一个函数 递归 带参数的装饰器 # 判断argv,当登录不同的网页,会有不同的装饰效果def auth(argv):   def warpp ...

  8. HNUSTOJ-1621 Picking Cabbage(状态压缩DP)

    1621: Picking Cabbage 时间限制: 2 Sec  内存限制: 32 MB提交: 26  解决: 14[提交][状态][讨论版] 题目描述 Once, Doraemon and  N ...

  9. node(koa2)跨域与获取cookie

    欲做一个node 的网关服务,通过 cookie 做信息传递,选择框架 koa2,这里简单记录跨域处理以及 cookie 获取. 首先:解决跨域问题,使用 koa2-cros 来处理,跨域问题后端处理 ...

  10. 你知道dos和cmd之间的关系以及区别吗?

    含义 dos 英文disk operation system,意思是磁盘操作系统是微软系列操作系统之一,dos是一个独立的操作系统,dos对操作人员的要求是比较高的,操作者需要记住很多的命令,并利用命 ...