全程 Link-Cut Tree,是解决动态树问题的有力科技

——题记

简单实现

LCT 的形态直观上是一堆 Splay 的合体,每个 Splay 以时间戳为关键字,各个 Splay 通过虚边相连,可以唯一还原出一棵树

Splay 的划分依据为实链剖分,即在一条实链上的点放到一棵 Splay 里面,其他关联点通过虚边和其他 Splay 连接

一条虚边体现为儿子的父亲认做父亲,而父亲是不认这个儿子的

首先得先写 Splay 对吧,唯二不同的是 Splay 之前先跳到根把所有懒标记下放,以及旋转是不能贸然让父亲认儿子,需要判断是不是虚边

核心函数 —— \(access(x)\) 操作

经过这个操作后可以把 \(x\) 这个节点与整棵树的树根划分在一条实链里(即一棵 Splay 里),并且这条链在 \(x\) 处结束

至于具体怎么弄就不说了,反正代码异常简洁

void access(int x){
for(int y=0;x;y=x,x=fa[x]){
splay(x);
son[x][1]=y;
update(x);
}
return ;
}

\(makeroot(x)\) 操作 —— 将 \(x\) 弄到整棵树的树根

先 \(access\),再 \(splay\),这时虽然它到了平衡树的顶部,由于存在左儿子,它的时间戳没变

这是用一个神奇的操作 —— \(reverse\),就像文艺平衡树那样把它两个儿子颠倒一下,这个也需要通过打懒标记依次下放

\(link(x,y)\) 操作 —— 将两个点所在的树合并

把 \(x\) 转到它那棵树的根,直接认 \(y\) 当爹即可

\(cut(x,y)\) 操作 —— 将两个点间的连边删除

把 \(x\) 弄到根上,把 \(y\) 和它打通,再把 \(y\) 转到根上,由于它们在树上是相邻的,这时的状态一定是 Splay 里只有它们两个,直接不认父亲和不认儿子即可

(注:这里的“弄”指的是 \(makeroot\),这里的“转”指的是 \(splay\),习惯上代码里把前三步重新封装到一个 \(split\) 函数里)

这些就是核心函数了,对于询问,常形如“从 \(x\) 到 \(y\) 的路径上的 xxx 信息”

这时只需要做一遍 \(split\) 操作,\(y\) 节点上的信息就是链上的信息

这里放一下洛谷模板题的代码,背熟即可:

代码
#include<bits/stdc++.h>
using namespace std;
int n,m,op,x,y;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
namespace LCT{
const int maxn=2e5+5;
int fa[maxn],son[maxn][2];
int rev[maxn],val[maxn],sum[maxn];
int sta[maxn],tp; void update(int x){
sum[x]=sum[son[x][0]]^sum[son[x][1]]^val[x];
return ;
}
void push_rev(int x){
rev[x]^=1;
swap(son[x][0],son[x][1]);
return ;
}
void down(int x){
if(rev[x]){
push_rev(son[x][0]);
push_rev(son[x][1]);
rev[x]=0;
}
return ;
}
bool get(int x){
return son[fa[x]][1]==x;
}
bool isroot(int x){
return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
}
void rotate(int x){
int f1=fa[x];
int f2=fa[f1];
bool id=get(x);
son[f1][id]=son[x][id^1];
fa[son[f1][id]]=f1;
if(!isroot(f1))son[f2][get(f1)]=x;
fa[x]=f2;
son[x][id^1]=f1;
fa[f1]=x;
update(f1);
return ;
}
void splay(int x){
tp=0;
int y=x;
sta[++tp]=y;
while(!isroot(y))sta[++tp]=y=fa[y];
while(tp)down(sta[tp--]);
while(!isroot(x)){
int f1=fa[x];
int f2=fa[f1];
if(!isroot(f1)){
rotate(get(x)==get(f1)?f1:x);
}
rotate(x);
}
update(x);
return ;
}
void access(int x){
for(int y=0;x;y=x,x=fa[x]){
splay(x);
son[x][1]=y;
update(x);
}
return ;
}
void makeroot(int x){
access(x);
splay(x);
push_rev(x);
return ;
}
void split(int x,int y){
makeroot(x);
access(y);
splay(y);
}
int findroot(int x){
access(x);
splay(x);
while(son[x][0])x=son[x][0];
splay(x);
return x;
}
void link(int x,int y){
makeroot(x);
if(findroot(y)!=x)fa[x]=y;
return ;
}
void cut(int x,int y){
split(x,y);
if(son[y][0]==x){
son[y][0]=fa[x]=0;
update(y);
}
return ;
}
int ask(int x,int y){
split(x,y);
return sum[y];
}
void modify(int x,int w){
splay(x);
val[x]=w;
update(x);
return ;
}
}
int main(){
n=read();
m=read();
for(int i=1;i<=n;i++){
LCT::val[i]=read();
}
for(int i=1;i<=m;i++){
op=read();
x=read();
y=read();
switch (op){
case 0:
printf("%d\n",LCT::ask(x,y));
break;
case 1:
LCT::link(x,y);
break;
case 2:
LCT::cut(x,y);
break;
case 3:
LCT::modify(x,y);
}
}
return 0;
}

LCT 应用

呼哧~

废了好半天才打完的板子,LCT一定很有用吧?这一点还是可以肯定的


维护连通性质

这本来是并查集干的活

如果只有加边,并查集得心应手

如果只有删边,倒过来就可以了

如果都有,还是LCT吧……

P2147 [SDOI2008]洞穴勘测

这就很板子了吧?如果联通那么他们所在树的根是相同的


维护链上信息

比如刚才那道模板题

再来一点儿复杂信息的模板题:

P1501 [国家集训队]Tree II

LCT模板+线段树模板2即可,懒标记和翻转标记一起 \(pushdown\) 即可


维护边权信息

可以把边替换成一个点,这个点分别向两端点连边,点权设为边权

B. 树的维护

LCT 小记的更多相关文章

  1. LCT小记

    不用说了,直接上怎么 die( 千万不要和 Treap 一样写左旋 zig 和右旋 zag,莫名死亡.Splay 只支持一个 rotate 上旋一个节点即可. splay() 之前记得弄一个栈存储 u ...

  2. 树上数据结构——LCT

    目录 树上数据结构--LCT 概述 基本概念 核心操作 其他操作 完整模板 树上数据结构--LCT 概述 LCT是一种强力的树上数据结构,支持以下操作: 链上求和 链上求最值 链上修改 子树修改 子树 ...

  3. [原]Paste.deploy 与 WSGI, keystone 小记

    Paste.deploy 与 WSGI, keystone 小记 名词解释: Paste.deploy 是一个WSGI工具包,用于更方便的管理WSGI应用, 可以通过配置文件,将WSGI应用加载起来. ...

  4. MySql 小记

    MySql  简单 小记 以备查看 1.sql概述 1.什么是sql? 2.sql发展过程? 3.sql标准与方言的关系? 4.常用数据库? 5.MySql数据库安装? 2.关键概念 表结构----- ...

  5. 一堆LCT板子

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

  6. Git小记

    Git简~介 Git是一个分布式版本控制系统,其他的版本控制系统我只用过SVN,但用的时间不长.大家都知道,分布式的好处多多,而且分布式已经包含了集中式的几乎所有功能.Linus创造Git的传奇经历就 ...

  7. 广州PostgreSQL用户会技术交流会小记 2015-9-19

    广州PostgreSQL用户会技术交流会小记 2015-9-19 今天去了广州PostgreSQL用户会组织的技术交流会 分别有两个session 第一个讲师介绍了他公司使用PostgreSQL-X2 ...

  8. 东哥读书小记 之 《MacTalk人生元编程》

         一直以来的自我感觉:自己是个记性偏弱的人.反正从小读书就喜欢做笔记(可自己的字写得巨丑无比,尼玛不科学呀),抄书这事儿真的就常发生俺的身上. 因为那时经常要背诵课文之类,反正为了怕自己忘记, ...

  9. Paypal支付小记

    Paypal支付小记 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impo ...

随机推荐

  1. Nacos 自动更新配置不生效问题

    版本 Nacos 1.4.1 SpringCloud 2020.0.3 解决方案 bootstrap.properties 增加应用名配置即可 spring.application.name=serv ...

  2. Spring源码解析之BeanFactoryPostProcessor(三)

    在上一章中笔者介绍了refresh()的<1>处是如何获取beanFactory对象,下面我们要来学习refresh()方法的<2>处是如何调用invokeBeanFactor ...

  3. k8s入门你至少需要会哪些

    body { margin: 0; overflow: auto; font: normal 14px Verdana; background: rgba(255, 255, 255, 1); pad ...

  4. AJAX的学习与使用>前端技术系列

    目录 AJAX的学习与使用 什么是AJAX 为什么要使用AJAX AJAX接收服务器响应数据的3种格式 文本格式(重要) JSON格式(重要) 服务器端响应实体类JSON格式的3种方式 修改实体类的t ...

  5. CentOS 8.0 安装docker 报错:Problem: package docker-ce-3:19.03.4-3.el7.x86_64 requires containerd.io >= 1

    1.错误内容 package docker-ce-3:19.03.2-3.el7.x86_64 requires containerd.io >= 1.2.2-3, but none of th ...

  6. vim的代码缩进

    例如下面一段代码: 现在要统一缩进6个tab,在UltraEdit里首先要选中文本,然后按6次tab,VIM中的操作是:首先按V(shift+v)进入行visual模式,然后按7次j选中这段文本(或者 ...

  7. 寻找写代码感觉(二)之 Spring Boot 项目属性配置

    一.前言 写代码就和恋爱一样,有反馈就要趁热打铁,搞完了项目搭建,接下来就来搞搞项目配置. 二.IDEA设置 1.编码配置 这里所说的就是代码的编码格式,你可以不设置,但是可能要面临的是,很多未知的麻 ...

  8. Python对系统数据进行采集监控——psutil

    大家好,我是辰哥- 今天给大家介绍一个可以获取当前系统信息的库--psutil 利用psutil库可以获取系统的一些信息,如cpu,内存等使用率,从而可以查看当前系统的使用情况,实时采集这些信息可以达 ...

  9. vivo商城计价中心 - 从容应对复杂场景价格计算

    一.背景 随着vivo商城的业务架构不断升级,整个商城较为复杂多变的营销玩法被拆分到独立的促销系统中. 拆分后的促销系统初期只是负责了营销活动玩法的维护,促销中最为重要的计价业务仍然遗留在商城主站业务 ...

  10. 【springcloud】springcloud与springboot的版本对应关系

    官方网址:https://start.spring.io/actuator/info 更新时间:2019-12-01 spring-cloud: "Finchley.M2": &q ...