首页 > 其他分享 >Splay&LCT不怎么详细的详解

Splay&LCT不怎么详细的详解

时间:2023-07-14 16:01:49浏览次数:47  
标签:rt splay LCT ch int Splay fa 详解

Splay:平衡树的一种,学名伸展树。

平衡树首先是一棵二叉搜索树(BST),满足性质:中序遍历单调递增。
根据这个性质,很容易在一棵 BST 上完成以下操作:插入一个数,查询一个数的排名,查询给定排名的数,删除一个数。
BST 可能是不平衡的,即左右子树相差很大。Splay 均摊后是平衡的,即时间复杂度均摊 \(O(n\log n)\)。
Splay 的基本操作是旋转rotate和伸展splay
rotate算是大部分平衡树都有的一个操作,它的作用是将一个结点由儿子变成父亲,且不改变 BST 的性质。旋转分左旋和右旋,可以理解为对左儿子右旋,对右儿子左旋(实际不用区分)。捞两张图来说明:


代码实现:

int rt,tot,fa[N],ch[N][2],val[N],cnt[N],siz[N];
//rt表示平衡树的根,fa表示结点的父亲,ch[u][0/1]表示u的左儿子/右儿子,val表示
//结点存储的值,cnt表示个数,siz表示子树大小
void push_up(int p){siz[p]=siz[ch[p][0]]+siz[ch[p][1]]+cnt[p];}
bool get(int p){return p==ch[fa[p]][1];}
void rotate(int x){//x是旋转中的儿子结点
	int y=fa[x],z=fa[y],op=get(x)^1;
	if(z)ch[z][get(z)]=x;//这句推荐写在前面。这里写后面没关系,但接下来LCT不行
	ch[y][op^1]=ch[x][op];if(ch[x][op])fa[ch[x][op]]=y;//更新x的儿子
	ch[x][op]=y;fa[y]=x;fa[x]=z;//更新x和y
	push_up(y);push_up(x);//不要忘记更新结点信息
}

splay表示把一个点转到根。如果一直单旋复杂度不对,要使用双旋。双旋,即当 \(x\) 和 \(fa[x]\) 是同侧儿子时,先转 \(fa[x]\),再转 \(x\),否则转两次 \(x\)(也可能只能转一次,因为 \(fa[x]=rt\))。有些时候我们并不想把 \(x\) 转到根,那同样很容易。代码实现:

void splay(int x,int goal=0){//将x转到goal
	for(int p=fa[x];p!=goal;p=fa[x]){
		if(fa[p]!=goal)rotate(get(p)==get(x)?p:x);
		rotate(x);//双旋
	}if(!goal)rt=x;//不要忘记可能要更新根结点
}

特别的,Splay的删除可以直接把一个点转到根,然后删除,将其左子树的最小值(一路向左找儿子)转到根,并将原来的右子树接回去。代码实现:

void merge(int x,int y){
	while(ch[x][1])x=ch[x][1];
	splay(x);ch[x][1]=y;fa[y]=x;push_up(x);
}
void del(int v){
	if(!find(v))return;
	if(cnt[rt]>1){--cnt[rt],--siz[rt];return;}
	int x=ch[rt][0],y=ch[rt][1];
	fa[x]=fa[y]=0;clear(rt);
	if(!x||!y){rt=x+y;return;}
	merge(x,y);
}

Splay 的一个技巧是把点类似线段树的结点用,打各种各样的标记。只要在每次找点(加点,查询排名,查询值)的时候pushdown即可。然后用操作splay可以提取一段区间 \([l,r]\),这只要splay(l-1);splay(r+1,l-1),然后 \(r+1\) 的左子树就是区间 \([l,r]\)。例题:洛谷P3391 文艺平衡树
更多的例题可以看 Solution Set - Splay

LCT:Link/Cut Tree。用来解决A+B problem动态树问题。这个问题中要维护一个森林,支持动态连边,删边和其它一些操作。

思想是做虚实链剖分,用 Splay 维护所有链(按照深度排序)。所谓的虚实链剖分,和重链剖分一样,都是给每个点选一条连向儿子的边作为重边/实边,然后维护这些链。不同之处在于实边完全可以随便选,反正Splay都可以维护。利用这一点,科学家发明了 LCT。
想象很多 Splay,每个维护一条实链。然后我们在一条链对应的 Splay 的根结点的父亲指向这条链原本的根结点。这就代表了一条虚边,它最重要的特点是“子认父,父不认子”。(建议把辅助树丢到一边去呢)
LCT 的核心操作包括打通Access和换根makeroot
打通Access,即把一个点到根的路径上所有边更改为实边。只要从某个点开始,不断对它伸展,然后跳父亲,改儿子即可。不要问时间复杂度,问就是 Splay 保证。
换根makeroot,只要先打通再伸展。但注意此时左右子树需要调换,类似于“文艺平衡树”打标记即可。
剩下的连边link,提取路径split,删边cut都很好实现了,直接看代码叭。

void Access(int x){
	for(int y=0;x;y=x,x=fa[x])
		splay(x),ch[x][1]=y,push_up(x);
}
int findRoot(int p){
	Access(p);splay(p);
	while(ls)push_down(p),p=ls;
	splay(p);return p;
}
//找一个点所在链的根,只要转到根后一路向左即可
void makeRoot(int p){Access(p);splay(p);make_tag(p);}
void split(int x,int y){makeRoot(x);Access(y);splay(y);}
bool link(int x,int y){
	makeRoot(x);
	if(findRoot(y)==x)return false;
	//有时候题目不保证连接的边一定不成环,那就要判一下是否已经是根
	fa[x]=y;return true;
}
void cut(int x,int y){split(x,y);ch[y][0]=fa[x]=0;push_up(y);}
//同样可能不保证删除的边存在,但最好直接开一个map记录,没必要整什么性质

例题看 Solution Set - LCT

标签:rt,splay,LCT,ch,int,Splay,fa,详解
From: https://www.cnblogs.com/by-chance/p/17553913.html

相关文章

  • Linux下chkconfig命令详解(service)
    Linux下chkconfig命令详解(service)一、释义chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息。谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接。二、使用语法chkconfig[--add][--del][--list][系统服务]或chkconfig[--level......
  • 2023年iOS App Store上架流程详解(上)
    ​ 在2023年,随着苹果发布机制的微调,有些关于iOSApp上架流程的资料已经过时。本文将根据最新的要求和经验,详细介绍iOSApp上架的流程。1.注册开发者账号首先,您需要注册一个AppleDeveloper的开发者账号。这个账号的年费大约是600多元人民币。注册过程可以在AppleDeveloper......
  • dede织梦标签,dede:arclist用法与详解
    标签名称:arclist标记简介:织梦常用标记,也称为自由列表标记,其中imglist、imginfolist、specart、coolart、autolist都是由该标记所定义的不同属性延伸出来的别名标记。功能说明:获取指定文档列表适用范围:全局使用基本语法:{dede:arclist?flag='h'typeid=''row=''col=''titlelen=......
  • ASP.Net Core Razor+AdminLTE 应用详解
     AdminLTE介绍 一个基于bootstrap的轻量级后台模板,这个前端界面个人感觉很清爽,对于一个大后端的我来说,可以减少较多的时间去承担前端的工作但又必须去独立去完成一个后台系统开发的任务,并且,文档还算比较齐全,对着demo可以完成一个基本的前端框架搭建了。大家如有更为好......
  • python之struct详解
    用处按照指定格式将Python数据转换为字符串,该字符串为字节流,如网络传输时,不能传输int,此时先将int转化为字节流,然后再发送;按照指定格式将字节流转换为Python指定的数据类型;处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理......
  • Spring的生命周期详解
    Spring的生命周期Spring框架是一个非常流行的Java企业级开发框架,它提供了很多强大的功能,包括依赖注入、AOP、事务管理等。在使用Spring框架时,了解Spring的生命周期非常重要,可以帮助我们更好地理解Spring框架的工作原理。Spring的生命周期可以分为三个阶段:实例化阶段、初始化阶段......
  • C#调用存储过程详解(带返回值、参数输入输出等)
    本文实例讲述了c#调用存储过程的方法。分享给大家供大家参考,具体如下:createprocedure[dbo].[getnamebyid]@studentidvarchar(8),@studentnamenvarchar(50)outputasbeginselect@studentname=studentnamefromstudentwherestudentid=@studentidif@@e......
  • 一文详解常见标准化组织
    从事软件研发工作多年,在工作中有时会查阅一些通信相关的国际标准。然而,对于制定这些标准的组织,一直缺乏一个系统的了解。本文将对几个常见的标准化组织进行介绍,其中包括ITU、3GPP、GSMA和CCSA,了解它们的背景、成立目的和主要任务。ITU国际电信联盟3GPP第三代合作伙伴计划G......
  • 一文详解 Okio 输入输出流
    在OkHttp的源码中,我们经常能看到Okio的身影,这篇文章,我们把Okio拿出来进行一个详细的介绍学习。输入输出的概念简述Okio简介工程中引入OkioAPI简介及使用介绍一、输入输出在正式介绍Okio之前,让我们先回忆一下输入/输出流的概念。输入流:外设——>内存将数据从各......
  • 详解在Linux中修改Tomcat使用的jdk版本
    问题分析由于部署个人项目使用了openjdk11,但是我之前安装的是jdk1.8,jdk版本升级的后果就是,tomcat运行的时候报一点小bug(因为之前安装tomcat默认使用了系统的jdk版本)所以就想着把tomcat使用的jdk版本调回原来的,找了很多资料之后,决定在tomcat的运行文件中覆盖使用的jdk版本路径......