图片参考YangZhe的论文FlashHu大佬的博客

Link-Cut-Tree实际靠的是实链剖分,重链剖分和长链剖分珂以参考树链剖分详解

Link-Cut-Tree将某一个儿子的连边划分为实边,而连向其他子树的边划分为虚边

区别在于虚实是可以动态变化的,因此要使用更高级、更灵活的Splay来维护每一条由若干实边连接而成的实链

请先学习Splay之后再阅读本文

Link-Cut-Tree功能强大,能维护以下东西:

  • 查询、修改链上的信息(最值,总和等)

  • 随意指定原树的根(即换根)

  • 动态连边、删边

  • 动态维护连通性

  • 更多毒瘤操作

Link-Cut-Tree的性质

1.每一个Splay维护的是一条从上到下按在原树中深度严格递增的路径,且中序遍历Splay得到的每个点的深度序列严格递增

2.每个节点包含且仅包含于一个Splay中

3.边分为实边和虚边,实边包含在Splay中,而虚边总是由一棵Splay指向另一个节点(指向该Splay中中序遍历最靠前的点在原树中的父亲)

因为性质2,当某点在原树中有多个儿子时,只能向其中一个儿子拉一条实链(只认一个儿子),而其它儿子是不能在这个Splay中的

那么为了保持树的形状,我们要让到其它儿子的边变为虚边,由对应儿子所属的Splay的根节点的父亲指向该点,而从该点并不能直接访问该儿子(认父不认子)

核心操作(以下代码以Luogu P3690 【模板】Link Cut Tree (动态树)为例)

一、access

是Link-Cut-Tree的核心操作也是最难理解的操作

假设有一珂树,有一棵树,一开始实边和虚边是这样划分的(虚线为虚边)

那么所构成的LCT可能会长这样(绿框中为一个Splay,可能不会长这样,但只要满足中序遍历按深度递增(性质1)就对结果无影响)

现在我们要access(N),把A~N的路径拉起来变成一条Splay

因为性质2,该路径上其它链都要给这条链让路,也就是把每个点到该路径以外的实边变虚

所以我们希望虚实边重新划分成这样

那么如何实现这个过程呢?

首先把splay(N),使之成为当前Splay中的根

为了满足性质2,原来N~O的重边要变轻

因为按深度O在N的下面,在Splay中O在N的右子树中,所以直接单方面将N的右儿子置为0(认父不认子)

然后就变成了这样——

我们接着把N所属Splay的虚边指向的I(在原树上是L的父亲)也转到它所属Splay的根,splay(I)

原来在I下方的重边I~K要变轻(同样是将右儿子去掉)

这时候I~L就可以变重了。因为L肯定是在I下方的(刚才L所属Splay指向了I),所以I的右儿子置为N,满足性质1。

或许看了这些聪明的你就能发现规律

剩下的步骤自己脑补

想使一个点到根之间的路径在同一个Splay中只需要循环执行以下操作:

1.转到根
2.换儿子
3.跟新
4.当前操作点切换为轻边所指的父亲
	inline void pushup(register int x)
{
xr[x]=xr[c[x][0]]^xr[c[x][1]]^val[x];
}
inline void pushdown(register int x){
if(rev[x])
{
register int l=c[x][0],r=c[x][1];
rev[l]^=1,rev[r]^=1,rev[x]^=1;
Swap(c[x][0],c[x][1]);
}
}
inline bool isroot(register int x)
{
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
inline void rotate(register int x)
{
int y=fa[x],z=fa[y],l,r;
l=c[y][0]==x?0:1;
r=l^1;
if(!isroot(y))
c[z][c[z][0]==y?0:1]=x;
fa[x]=z;
fa[y]=x;
fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
pushup(y),pushup(x);
}
inline void splay(register int x)
{
top=1;
q[top]=x;
for(register int i=x;!isroot(i);i=fa[i])
q[++top]=fa[i];
for(register int i=top;i;--i)
pushdown(q[i]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
rotate((c[y][0]==x)^(c[z][0]==y)?(x):(y));
rotate(x);
}
}
inline void access(register int x)
{
for(register int t=0;x;t=x,x=fa[x])
{
splay(x);
c[x][1]=t;
pushup(x);
}
}

pushdown就跟懒标记差不多(珂以先不看)

二、makeroot

makeroot定义为换根,让指定点成为原树的根

这时候就利用到access(x)和Splay的翻转操作

access(x)后x在Splay中一定是深度最大的点。

splay(x)后,x在Splay中将没有右子树(性质1)。于是翻转整个Splay,使得所有点的深度都倒过来了,x没了左子树,反倒成了深度最小的点(根节点),达到了我们的目的

	inline void makeroot(register int x)
{
access(x);
splay(x);
rev[x]^=1;
}

三、findroot

找x所在原树的树根,主要用来判断两点之间的连通性(findroot(x)==findroot(y)表明x,y在同一棵树中)

inline int findroot(register int x)
{
access(x);
splay(x);
while(c[x][0])
x=c[x][0];
return x;
}

四、link

在x,y两点之间连边

只在保证题目数据合法的情况下才能使用(不一定合法的话先要判联通(findroot))

	inline void link(register int x,register int y)
{
makeroot(x);
fa[x]=y;
}

五、cut

将x,y之间的边切断

	inline void split(register int x,register int y)
{
makeroot(x);
access(y);
splay(y);
}
inline void cut(register int x,register int y)
{
split(x,y);
if(c[y][0]==x)
{
c[y][0]=0;
fa[x]=0;
}
}

完整代码

#include <bits/stdc++.h>
#define N 300005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline void Swap(register int &a,register int &b)
{
a^=b^=a^=b;
}
int n,m,val[N];
struct Link_Cut_Tree{
int c[N][2],fa[N],top,q[N],xr[N],rev[N];
inline void pushup(register int x)
{
xr[x]=xr[c[x][0]]^xr[c[x][1]]^val[x];
}
inline void pushdown(register int x){
if(rev[x])
{
register int l=c[x][0],r=c[x][1];
rev[l]^=1,rev[r]^=1,rev[x]^=1;
Swap(c[x][0],c[x][1]);
}
}
inline bool isroot(register int x)
{
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
inline void rotate(register int x)
{
int y=fa[x],z=fa[y],l,r;
l=c[y][0]==x?0:1;
r=l^1;
if(!isroot(y))
c[z][c[z][0]==y?0:1]=x;
fa[x]=z;
fa[y]=x;
fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
pushup(y),pushup(x);
}
inline void splay(register int x)
{
top=1;
q[top]=x;
for(register int i=x;!isroot(i);i=fa[i])
q[++top]=fa[i];
for(register int i=top;i;--i)
pushdown(q[i]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
rotate((c[y][0]==x)^(c[z][0]==y)?(x):(y));
rotate(x);
}
}
inline void access(register int x)
{
for(register int t=0;x;t=x,x=fa[x])
{
splay(x);
c[x][1]=t;
pushup(x);
}
}
inline void makeroot(register int x)
{
access(x);
splay(x);
rev[x]^=1;
}
inline int findroot(register int x)
{
access(x);
splay(x);
while(c[x][0])
x=c[x][0];
return x;
}
inline void split(register int x,register int y)
{
makeroot(x);
access(y);
splay(y);
}
inline void cut(register int x,register int y)
{
split(x,y);
if(c[y][0]==x)
{
c[y][0]=0;
fa[x]=0;
}
}
inline void link(register int x,register int y)
{
makeroot(x);
fa[x]=y;
}
}T;
int main()
{
n=read(),m=read();
for(register int i=1;i<=n;++i)
{
val[i]=read();
T.xr[i]=val[i];
}
while(m--)
{
int opt=read();
if(opt==0)
{
int x=read(),y=read();
T.split(x,y);
write(T.xr[y]),puts("");
}
else if(opt==1)
{
int x=read(),y=read();
if(T.findroot(x)!=T.findroot(y))
T.link(x,y);
}
else if(opt==2)
{
int x=read(),y=read();
T.cut(x,y);
}
else
{
int x=read(),y=read();
T.access(x);
T.splay(x);
val[x]=y;
T.pushup(x);
}
}
return 0;
}

Link-Cut-Tree详解的更多相关文章

  1. link cut tree 入门

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

  2. Codeforces Round #339 (Div. 2) A. Link/Cut Tree 水题

    A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...

  3. Linux 之Cut命令详解

    摘自:http://blog.csdn.net/zsf8701/article/details/7718680 Linux 之Cut命令详解 cut是一个选取命令,就是将一段数据经过分析,取出我们想要 ...

  4. Link/cut Tree

    Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...

  5. [转帖]Linux:cut命令详解

    Linux:cut命令详解 https://www.cnblogs.com/Spiro-K/p/6361646.html cut -f cut -f -d cut -c1- 这三个命令好像最常见, 记 ...

  6. 洛谷P3690 Link Cut Tree (模板)

    Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...

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

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

  8. bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

    link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...

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

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

  10. Link Cut Tree学习笔记

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

随机推荐

  1. Nginx子域名配置

    extends:http://blog.csdn.net/xiaoping0915/article/details/53899465 ,http://www.myhack58.com/Article/ ...

  2. QT上位机

    程序是用QT写的,通过COM口进行数据的读取. 源码地址:https://github.com/kunkunlin/QT-SWJ

  3. react 简单的用函数调出ui显示

    import test from '../components/test' const info = () => { test.info('This is a normal message'); ...

  4. JavaScript 运行机制详解

    一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Java ...

  5. Coffee and Coursework (Hard Version)

    Coffee and Coursework (Hard Version) time limit per test 2.5 seconds memory limit per test 256 megab ...

  6. python中文分词库——pyltp

    pyltp在win10下安装比较麻烦,因此参考以下安装方式, 1.下载 win10下python3.6 2.安装 下载好了以后, 在命令行下, cd到wheel文件所在的目录, 然后使用命令pip i ...

  7. vue前后分离---数据模拟

    最近为在做CRM的前期工作,忙里偷闲写了个关于数据模拟方面的东西 主要是现在博客中满天都再说前后分离,但是还没有几个实际操作的---让许多新手{-_-} 方法一: 启动一个express静态服务器-- ...

  8. MyBatis映射配置文件详解

    <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-// ...

  9. Gym 101873G - Water Testing - [皮克定理]

    题目链接:http://codeforces.com/gym/101873/problem/G 题意: 在点阵上,给出 $N$ 个点的坐标(全部都是在格点上),将它们按顺序连接可以构成一个多边形,求该 ...

  10. iOS之关于Architectures设置及Build Active Architecture Only编译设置

    一,前言 最近在帮朋友解决极光报错的提示:“file was built for archive which is not the architecture being linked (i386)”时 ...