LCT(Link-Cut Tree,动态树)是一个支持动态修改树的结构的数据结构,其基本操作有 \(\texttt{access}\) , \(\texttt{findroot}\) , \(\texttt{makeroot}\), 其核心操作有 \(\texttt{link}\) 和 \(\texttt{cut}\) .

本文具体讲述 LCT 的操作内容和实际运用,不深入讨论如何得到每个具体操作。

1. LCT 简介

这里用到树链剖分的思想。LCT 的本质是动态维护链剖分。我们将树链剖分,分为实链和虚链,每一条实链用辅助树即平衡树(选择splay)来维护链上信息,每一条虚链连接各个辅助树,每棵辅助树按照深度为关键字维护。每次我们动态进行实链剖分来完成对树上信息和结构的操作。

2. LCT 的实现

接下来介绍 LCT 的各种操作:

2.1. ACCESS

\(\texttt{access(x)}\) 是 LCT 最基本的操作,功能为将根到 \(x\) 的一条链变为实链。这样的用途当然是将这段路径放入一棵辅助树,方便进行操作。

这里不再手玩,直接给出操作过程 (设 \(y\) 为 \(x\) 新实链上的儿子):

  1. 将 \(x\) 旋转到当前辅助树的根
  2. 将 \(x\) 辅助树中的右儿子改为 \(y\) 。
  3. \(y \leftarrow x, x\leftarrow fa[x]\) ,向上递归操作。

具体可以这样理解:将 \(x\) splay 后,他的右儿子比 \(x\) 的深度大,即为一条实链,那我们就断掉这条实链,把 \(y\) 接上去,具体实现即为直接修改右儿子。

for(int y=0;x;y=x,x=fa[x]) splay(x),ch[x][1]=y,pushup(x);

2.2. MAKEROOT

\(\texttt{makeroot(x)}\) 即将 \(x\) 提到原树的根。具体操作:我们 \(\texttt{access(x),splay(x)}\) 后,\(x\) 变为当前辅助树的根,且由于其深度最大,所以没有右子树。然后我们把左右子树翻转,这样就没有比 \(x\) 深度更小的点了, \(x\) 就变成根啦。

access(x),splay(x),rev[x]^=1;

2.3. FINDROOT

\(\texttt{findroot(x)}\) 即为找 \(x\) 所在原树的树根(注意:LCT维护的不一定是一棵树,可能是森林!)。

具体操作: \(\texttt{access(x),splay(x)}\) 后,一路走到最左边即可。很好理解。当然要记得 \(\texttt{pushdown}\) .

特别注意:最后要 splay(x) 保证复杂度!!!

access(x),splay(x); int y=x;
while(ch[y][0]) pushdown(y),y=ch[y][0];
splay(x); return y;

(PS:这可能是LCT基本操作里最长的函数了……)

(PS: 在只有 link 操作的题目可能用并查集更简单)

2.4. LINK

\(\texttt{link(x,y)}\) 即为连接两个节点。直接 \(\texttt{makeroot(x)}\) 后将 \(x\) 的父亲变为 \(y\) 即可。

在 \(\texttt{link}\) 操作不保证合法的情况下,还需要判断 \(y\) 的根是否为 \(x\) 。(代码未涉及)

makeroot(x),fa[x]=y;

2.5. SPLIT

不算是基本操作,但也很常用且简单。 \(\texttt{split(x,y)}\) 即为将 \(x\rightarrow y\) 的一条路径拉出来。我们将 \(x\) 置为根后 \(\texttt{access(y)}\) 即可。再加上 \(\texttt{splay(y)}\) 即可直接操作 \(y\) ,\(x\) 即为 \(y\) 的左儿子。

makeroot(x),access(y),splay(y);

2.6. CUT

\(\texttt{cut(x,y)}\) 即为断掉 \(x\rightarrow y\) 的这条边。 \(\texttt{split(x,y)}\) 之后直接将 \(fa[x]\) 和 \(ch[y][0]\) 置为 \(0\) 即可。

split(x,y),fa[x]=ch[y][0]=0;

在 \(\texttt{cut}\) 操作不保证合法的情况下,在 \(\texttt{makeroot}\) 后需判断:

if(findroot(y)==x&&f[y]==x&&!c[y][0]) do...

3. 辅助树

辅助树之前说过了,用splay来实现。接下来具体讲一讲 LCT 中的 splay 中的变化。

这里先直接贴代码:

#define isnrt(x) (ch[fa[x]][0]==x||ch[fa[x]][1]==x)
void pushdown(int x)
{
if(rev[x])
{
rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);
rev[x]=0;
}
}
void rotate(int x){
int y=fa[x],z=fa[y];
bool k=ch[y][0]==x; int w=ch[x][k];
if(isnrt(y))ch[z][ch[z][1]==y]=x;ch[x][k]=y;ch[y][!k]=w;
fa[w]=y;fa[y]=x;fa[x]=z;
}
void splay(int x)
{
int y=x,tp=1; st[1]=y;
while(isnrt(y)) st[++tp]=y=fa[y];
while(tp) pushdown(st[tp--]);
while(isnrt(x))
{
int y=fa[x],z=fa[y];
if(isnrt(y)) (ch[z][1]==y)^(ch[y][1]==x)?rotate(x):rotate(y);
rotate(x);
}
}

变化很容易看出:当前辅助树的根需要特判(代码中用 \(\texttt{isnrt(x)}\) 来实现)。具体的不再解释了。同时注意 \(\texttt{pushdown}\) .

4. 模板

模板题:给你一棵树,每个节点上一个权值,支持加边删边,修改单点权值,查询路径 \(\texttt{xor}\) 和 .

代码:略。

关于 LCT 的具体用途和题目,在(2)中详细讲解。

LCT(1)的更多相关文章

  1. 一堆LCT板子

    搞了一上午LCT,真是累死了-- 以前总觉得LCT高大上不好学不好打,今天打了几遍感觉还可以嘛= =反正现在的水平应付不太难的LCT题也够用了,就这样好了,接下来专心搞网络流. 话说以前一直YY不出来 ...

  2. 动态树之LCT(link-cut tree)讲解

    动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作.其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是LCT( ...

  3. 在此为LCT开一个永久的坑

    其实我连splay都还不怎么会. 今天先抄了黄学长的bzoj2049,以后一定要把它理解了. 写LCT怎么能不%数据结构大神yeweining呢?%%%chrysanthemums  %%%切掉大森林 ...

  4. 【BZOJ2157】旅游 LCT

    模板T,SB的DMoon..其实样例也是中国好样例...一开始不会复制,yangyang:找到“sample input”按住shift,按page down.... #include <ios ...

  5. 【BZOJ3669】[Noi2014]魔法森林 LCT

    终于不是裸的LCT了...然而一开始一眼看上去这是kruskal..不对,题目要求1->n的路径上的每个点的两个最大权值和最小,这样便可以用LCT来维护一个最小生成路(瞎编的...),先以a为关 ...

  6. 【BZOJ1180】: [CROATIAN2009]OTOCI & 2843: 极地旅行社 LCT

    竟然卡了我....忘记在push_down先下传父亲的信息了....还有splay里for():卡了我10min,但是双倍经验还是挺爽的,什么都不用改. 感觉做的全是模板题,太水啦,不能这么水了... ...

  7. 【BZOJ3282】Tree LCT

    1A爽,感觉又对指针重怀信心了呢= =,模板题,注意单点修改时splay就好,其实按吾本意是没写的也A了,不过应该加上能更好维护平衡性. ..还是得加上好= = #include <iostre ...

  8. BZOJ2888 资源运输(LCT启发式合并)

    这道题目太神啦! 我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去.那么这样一来看上去似乎就是一次操作O(nlogn),但是我们拥有数据结构的合并利器--启发式 ...

  9. LCT裸题泛做

    ①洞穴勘测 bzoj2049 题意:由若干个操作,每次加入/删除两点间的一条边,询问某两点是否连通.保证任意时刻图都是一个森林.(两点之间至多只有一条路径) 这就是个link+cut+find roo ...

  10. 链剖&LCT总结

    在搞LCT之前,我们不妨再看看喜闻乐见的树链剖分. 树链剖分有一道喜闻乐见的例题:NOI2015 软件包管理器 如果你看懂题目了,你就会明白它是叫你维护一个树,这棵树是不会动的,要兹磁子树求和,子树修 ...

随机推荐

  1. 小程序PromiseAll定义

    var promiseAll = (funcs, callback) =>{ var promises = [] for(var i=0; i<funcs.length; i++){ pr ...

  2. Nginx 不区分大小写

    location ~* .*\.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|wmf|js)$ {       #         slowfs_cache    fastca ...

  3. logj4.xml

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration PU ...

  4. java学习-循环结构-查找算法-顺序查找

    今天回顾了简单算法,顺序查找.发现了数组出现重复数字,无法输出第二个位置就跳出循环了. 利用所学知识解决了.放上代码,同时在代码里给大家分享思路. 欢迎大神教导,欢迎指正. ; System.out. ...

  5. JavaWeb开发:从购买服务器到简单demo运行

    写这篇文章的目的: 一个是为了记录实施过程,方便自己日后查阅: 另一个是给项目组成员提供一个参考,方便他们以后搭建自己的项目环境: 当然若能帮助到更多的朋友,那就再好不过了:D 需要注意: 我本身也是 ...

  6. 学习angularJs(1)--引用文件

    <script src="http://cdn.static.runoob.com/libs/angular.js/1.4.6/angular.min.js">< ...

  7. 可旋转Treap(树堆)总结

    树堆,在数据结构中也称Treap,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树.其基本操作的期望时间复杂度为O(logn).相对于其他的平衡二叉搜索树,Trea ...

  8. Object.getOwnPropertyDescriptors()

    Object.getOwnPropertyDescriptors() 前面说过,Object.getOwnPropertyDescriptor方法会返回某个对象属性的描述对象(descriptor). ...

  9. 吴裕雄--天生自然JAVA数据库编程:事务处理

    DROP TABLE user ; -- 删除表 CREATE TABLE user( id INT AUTO_INCREMENT PRIMARY KEY , name VARCHAR(30) NOT ...

  10. 再次实践 MySQL chart【转】

    学习了 chart 结构和模板的知识后,现在重新实践一次 MySQL chart,相信会有更多收获. chart 安装前的准备 作为准备工作,安装之前需要先清楚 chart 的使用方法.这些信息通常记 ...