Link Cat Tree (连喵树) 学习笔记
Link Cat Tree
一、感性定义
所谓连喵树,即一种对森林支持修改,查询,连边,删边等操作的数据结构(姑且算她是吧)。她用一颗颗互相连接的辅助树维护原森林的信息,辅助树相互连接的边叫虚边,辅助树内相互连接的边叫实边
二、关于辅助树和原森林
1.辅助树的点代表的就是原森林的点,一般我们选取splay作为辅助树。
以原树中节点的深度作为二叉排序树的权值。也就是说,如果我们中序遍历splay,得到的节点深度是严格递增的。
附注:这里的“原树”指“原森林”中的一颗树,下同。
2.辅助树维护的是原树中一条链,因为我们已经保证维护了点的深度,所以点在splay中的形态是不定的,splay互相的形态也可能不同
比如对这个这个原树,它本来是这个样子的
我们用辅助树对它进行操作后,它可能是这个样子的(虚线是虚边,颜色相同为一颗splay)
也可能是这个样子的
还可能是这个样子的
总结一下:我们确保splay中的边是真实的边\(^①\)且保证深度满足要求,而splay互相连接的边不一定是真实的边,它只是代表这两个splay有连接
3.边的表示及存储
如果两个点的父子关系是相互的,那么这条边是实边,两个点在一个splay中
如果两个点的父子关系不是相互的,即一个点的父亲的儿子中确没有自己,代表这是一条虚边,表示splay之间有连接
我们发现,只有splay中的根节点可能存在一条虚边。并且如果这条根节点没有虚边,那么就代表这个点是原树的根。
4.换根
为了很好的查询信息,我们需要能够替换原树的根的操作(后面讲)
三、操作与实现
(1)★\(Access(x)\)★
作用:表示将\(x\)向原树根节点打通一条链,并将这条链搞到一颗splay里面
操作:暴力向上找父亲跳(当然跳的是splay),把右儿子置为跳过来的splay并更新信息
Code:
void access(int now)//在辅助树中打通一条到原树的链
{
for(int las=0;now;las=now,now=fa)
splay(now),rs=las,updata(now);
}
这个操作是核心,请好好理解
(2)\(evert(x)\)
作用:将\(x\)置为原树的根节点
操作:首先我们把树打通一条链到根,然后\(splay\)到根。为了保证深度越小的点在中序遍历越靠前,我们要像文艺平衡树一样,把区间给翻转,带上标记
Code:
void evert(int now)//将节点提至原树根节点
{
access(now),splay(now),Reverse(now);//打通,丢上去,翻转
}
(3)\(findroot(x)\)
作用:找到\(x\)所在原树的根
操作:先打通一条链到根,然后splay上去,最左边的节点就是根了
Code:
int findroot(int now)//寻找原树根节点
{
access(now),splay(now);
while(ls) now=ls;
return now;
}
(4)\(split(x,y)\)
这个操作可以不写,为了方便我们才写它
作用:把\(x\)到\(y\)搞成一条路径放在一颗splay里并且\(y\)为splay的根
操作:把\(x\)放到它所在原树的根,然后把\(y\)给splay上去
我们可以不保证它们一定在一颗原树里,当做无效操作即可
Code:
void split(int u,int v)//把链抽进辅助树(可以不在一颗树)
{
evert(u),access(v),splay(v);//放至根,打通,丢上去
}
(5)\(link(x,y)\)
作用:连接节点\(x\)和节点\(y\)
操作:把\(x\)变成它所在树的根,然后只把\(x\)的父亲改成\(y\)(连虚边)
如果要不保证合法性,加个判断就行
Code:
void link(int u,int v)//连边
{
evert(u);//搞到根
if(findroot(v)!=u)//保证不在一颗树
par[u]=v;//只连了虚边
}
(6)\(cat(x,y)\)
作用:切断节点\(x\)与节点\(y\)之间的边
操作:把\(x\)和\(y\)搞到一颗splay里面,然后断双向边且更新答案。
如果不保证合法性,同样加个判断就行
Code:
void cat(int u,int v)//断边
{
split(u,v);
if(ch[v][0]==u)//直接相连
par[u]=ch[v][0]=0,updata(v);//双向断边注意更新
}
(7)其他
其他除了前两个操作,其他操作基本可以做的更小的常数,但是以上的比较好理解。
还有一些其他的询问操作或者修改操作就因题而异了。
四、与原先splay的不同之处
(1)\(isroot(x)\)
判断节点\(x\)是不是splay的根
Code:
bool isroot(int now)//判断是否为子树的根
{
return ch[fa][0]==now||ch[fa][1]==now;
}
(2)旋转时
旋转时不要连多了
因为大家旋转可能写的都不一样,所以就不放代码了
(3)splay时
我们得先找到到根的那条链,然后从上往下把翻转标记下发,然后再进行旋转
Code:
void splay(int now)
{
int tot=0;
while(isroot(now)) s[++tot]=now,now=fa;//先拿栈存储链
s[++tot]=now;
while(tot) pushdown(s[tot--]);//从上往下下放
now=s[1];
for(;isroot(now);Rotate(now))
if(isroot(fa))
Rotate(identity(now)^identity(fa)?now:fa);
}
五、洛谷P3690 【模板】Link Cut Tree (动态树)代码参考
Code:
#include <cstdio>
#define ls ch[now][0]
#define rs ch[now][1]
#define fa par[now]
const int N=3e5+10;
int ch[N][2],dat[N],sum[N],tag[N],par[N],s[N],n,m,tmp;
void updata(int now)//正常的更新
{
sum[now]=sum[ls]^sum[rs]^dat[now];
}
bool isroot(int now)//判断是否为子树的根
{
return ch[fa][0]==now||ch[fa][1]==now;
}
int identity(int now)//判断是哪个儿子
{
return ch[fa][1]==now;
}
void connect(int f,int now,int typ)//确定双向父子关系
{
fa=f;ch[f][typ]=now;
}
void Reverse(int now)//区间翻转,标记管儿子
{
tmp=ls,ls=rs,rs=tmp,tag[now]^=1;
}
void pushdown(int now)//下发标记
{
if(tag[now])
{
if(ls) Reverse(ls);
if(rs) Reverse(rs);
tag[now]^=1;
}
}
void Rotate(int now)//旋转
{
int p=fa,typ=identity(now);
connect(p,ch[now][typ^1],typ);
if(isroot(p)) connect(par[p],now,identity(p));//注意判断父亲是否为辅助树的根节点
else fa=par[p];
connect(now,p,typ^1);
updata(p),updata(now);
}
void splay(int now)
{
int tot=0;
while(isroot(now)) s[++tot]=now,now=fa;//先拿栈存储链
s[++tot]=now;
while(tot) pushdown(s[tot--]);//从上往下下放
now=s[1];
for(;isroot(now);Rotate(now))
if(isroot(fa))
Rotate(identity(now)^identity(fa)?now:fa);
}
void access(int now)//在辅助树中打通一条到原树的链
{
for(int las=0;now;las=now,now=fa)
splay(now),rs=las,updata(now);
}
void evert(int now)//将节点提至原树根节点
{
access(now),splay(now),Reverse(now);//打通,丢上去,翻转
}
void split(int u,int v)//把链抽进辅助树(可以不在一颗辅助树)
{
evert(u),access(v),splay(v);//放至根,打通,丢上去
}
int findroot(int now)//寻找原树根节点
{
access(now),splay(now);
while(ls) now=ls;
return now;
}
void link(int u,int v)//连边
{
evert(u);//搞到根
if(findroot(v)!=u)//保证不在一颗树
par[u]=v;//只连了虚边
}
void cat(int u,int v)//断边
{
split(u,v);
if(ch[v][0]==u)//直接相连
par[u]=ch[v][0]=0,updata(v);//双向断边注意更新
}
int query(int u,int v)
{
split(u,v);
return sum[v];
}
void change(int u,int x)
{
splay(u),dat[u]=x,updata(u);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d\n",dat+i);
for(int opt,u,v,i=1;i<=m;i++)
{
scanf("%d%d%d",&opt,&u,&v);
if(opt==0) printf("%d\n",query(u,v));
else if(opt==1) link(u,v);
else if(opt==2) cat(u,v);
else change(u,v);
}
return 0;
}
六、特别感谢
高级数据结构【林厚从】
网上许多大神的博客
七、updata
① 事实上,因为\(splay\)会旋转,所以并不一定是真实被连接的边,只是满足了深度关系
我们可以想一想这两种\(cat\)方式的区别
void cat(int u,int v)
{
evert(u);
access(v);
splay(v);
ch[v][0]=par[u]=0;
}
void cat(int u,int v)
{
evert(u);
access(v);
ch[u][1]=par[v]=0;
}
2018.8.11
Link Cat Tree (连喵树) 学习笔记的更多相关文章
- 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解
什么是线段树 线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值.区间和等问题. 线段树的用处 对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log n). 基础 ...
- zkw线段树学习笔记
zkw线段树学习笔记 今天模拟赛线段树被卡常了,由于我自带常数 \(buff\),所以学了下zkw线段树. 平常的线段树无论是修改还是查询,都是从根开始递归找到区间的,而zkw线段树直接从叶子结点开始 ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- P3690 【模板】Link Cut Tree (动态树)
P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...
- LuoguP3690 【模板】Link Cut Tree (动态树) LCT模板
P3690 [模板]Link Cut Tree (动态树) 题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两 ...
- Splay伸展树学习笔记
Splay伸展树 有篇Splay入门必看文章 —— CSDN链接 经典引文 空间效率:O(n) 时间效率:O(log n)插入.查找.删除 创造者:Daniel Sleator 和 Robert Ta ...
- Treap-平衡树学习笔记
平衡树-Treap学习笔记 最近刚学了Treap 发现这种数据结构真的是--妙啊妙啊~~ 咳咳.... 所以发一发博客,也是为了加深蒟蒻自己的理解 顺便帮助一下各位小伙伴们 切入正题 Treap的结构 ...
- 【刷题】洛谷 P3690 【模板】Link Cut Tree (动态树)
题目背景 动态树 题目描述 给定n个点以及每个点的权值,要你处理接下来的m个操作.操作有4种.操作从0到3编号.点从1到n编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor ...
- JSOI2008 Blue Mary开公司 | 李超线段树学习笔记
题目链接:戳我 这相当于是一个李超线段树的模板qwqwq,题解就不多说了. 代码如下: #include<iostream> #include<cstdio> #include ...
随机推荐
- php 操作RabbitMQ
本文摘抄自:https://www.cnblogs.com/alin-qu/p/8312874.html php 操作RabbitMQ 基本流程图 如果exchange 没有绑定queue,则消息 ...
- Delphi 过程类型
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...
- ecshop 漏洞如何修复 补丁升级与安全修复详情
目前ecshop漏洞大面积爆发,包括最新版的ecshop 3.0,ecshop 4.0,ecshop2.7.3全系列版本都存在着高危网站漏洞,导致网站被黑,被篡改,被挂马,许多商城系统深受其漏洞的攻击 ...
- 关于JUnit4无法支持多线程测试的解决方法
转自:https://segmentfault.com/a/1190000003762719 其实junit是将test作为参数传递给了TestRunner的main函数.并通过main函数进行执行. ...
- R语言绘图:直方图
使用ggplot2包绘制直方图 ######*****绘制直方图代码*****####### data1 <- data0[(data0[, 2] <= 500) & (data0 ...
- JS 实现AJAX封装(只限于异步)
1.AJAX 分为异步 和 同步 请求 比如你去买一个食品,但是商店暂时没有这个食品 异步:等到商品有了再来买,这个期间我可以去做别的事: 同步:一直在这里等,什么时候商品来了,买到手了,再去做别的事 ...
- 4269: 再见Xor
4269: 再见Xor 链接 分析: 和SGU 275唯一不同的就是需要求出次小值,那么异或出最大值的所有元素中,找到最小的,去除即可. 代码: #include<bits/stdc++.h&g ...
- RTSC和XDCTool的理解
1. 在使用TI的开发工具CCS中,里面有几个重要的概念,一直不太清晰,RTSC是什么,XDCTool是什么?包是什么?包的版本为啥都是4位的(比如mathlib_c66x_3_0_1_1)?star ...
- Gradle下载及安装教程
Gradle是基于Groovy语言的项目自动化建构工具,在使用Gradle之前常用的构建工具有Ant和Maven,使用这些工具我们可以用来管理项目依赖,打包,部署和发布等.使用Gradle我们将需要的 ...
- Python第三方库之openpyxl(3)
Python第三方库之openpyxl(3) 区域图 区域图类似于折线图,绘图线下面的区域会被填充,通过将分组设置为“standard”.“stacked”或“percentStacked”,可以获得 ...