LCT(1)
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\) 新实链上的儿子):
- 将 \(x\) 旋转到当前辅助树的根
- 将 \(x\) 辅助树中的右儿子改为 \(y\) 。
- \(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)中详细讲解。
最新文章
- Javascript之函数模型
- Oracle Segments可以跨多个data files吗?
- 排序命令sort
- C++ Socket UDP ";Hello World!";
- PhoneGap and Titanium
- 一个挺好用的任务提示小软件 Rainlendar2
- HDU2037今年暑假不AC(贪心)
- 超强JavaScript编辑器WebStorm代码提示迟缓问题及其它想到的
- 【IOS 开发】Object - C 语法 之 流程控制
- python基础----1. globals和locals
- for循环输出菱形的形状【java】
- ASP.NET MVC下使用AngularJs语言(四):$window.alert
- SpingBoot —— 多线程
- ansible报错:Failed to connect to the host via ssh: Permission denied
- OpenCV Mat数据类型及位数总结(转载)
- [ 转载 ] Java中常用的设计模式
- dict文档
- node事件循环
- 【bzoj4721】[Noip2016]蚯蚓 乱搞
- 在Code::Blocks中编译和使用wxWidgets3.0.0教程
热门文章
- Java程序员所需要掌握的核心知识
- css元素隐藏方式
- 浅谈Windows入侵检查
- 【SqlServer】利用sql语句附加,分离数据库-----转载
- 在fragment中实现返回键单击提醒 双击退出
- centos8 安装mysql 8.0
- SPring整合Mybatis方式一
- [AHK]输入法状态提示,中文状态提示“中”,英文状态提示“EN”[转]
- 在CentOS 7环境下安装 Spark
- PYTHON __main__