splay复习小记

article/2025/11/9 15:58:48

简介

splay的原名是伸展树,一种超级实用的数据结构,能快速地干很多数据结构不能干的事情。
很久以前就听说过并且略微地学了一些,但是当时了解地并不是很多。
有些人把splay达成spaly叫做死吧你,(⊙﹏⊙)b

结构

实质上他是一个二叉搜索树,就是每个节点的左儿子在原序列中是在自己左边的,右儿子在原序列中是在自己右边的,构图的方式有很多。
每一个节点都可以存储一些值,表示它的子树中的信息(比如说什么最大值啊,和啊之内的)。

构图

fo(i,1,n){scanf("%d",&a[i+1]);f[i]=i+1;t[i+1][0]=i;update(i+1);}f[n+1]=root=n+2,t[n+2][0]=n+1;update(n+2);

初学者的构图可以构成一条链。这样很明显左儿子在原序列上的位置是在自己左边的了。

但是有一个很奇妙的问题,为什么从2号点开始建?为什么建完点还要再向上多开一个n+2号点?

其实这种打法可以避免后面的特判,现在的1号点和n+2号点是建立在首尾两段的,如果不建立这两个点2号点的儿子和n+1号点的父亲都会指向0并传递信息,但是首尾建立一个虚点在修改操作中可以更方便的操作。比如说旋转的时候要涉及到首尾的时候,如果没有虚点,无法把首尾单独放到一个子树中去。

其实建成一条链在后面的操作会很慢。

int build(int l,int r,int y){if(l>r)return 0;int mid=(l+r)/2;int x=insert(a[mid]);f[x]=y;if(l==r)return x;tt[x][0]=build(l,mid-1,x);tt[x][1]=build(mid+1,r,x);update(x);return x;
}
root=build(0,n+1,0);

insert是建点操作,到后面再讲。
这样一开始就建成一颗二叉树会比较快。

功能

旋转

首先讲一个重要的部分,就是旋转。
这里写图片描述
其实splay这颗二叉树的中序遍历就是原序列,例如图中的原序列就是:AXBYC。现在我们要把x旋转到y的位置上,但是不能改变这棵树的中序遍历(及在原序列的先后顺序)。

bool son(int x){if(tt[f[x]][0]==x)return 0;return 1;
}
void rotate(int x){int y=f[x],z=son(x);tt[y][z]=tt[x][1-z];if(tt[x][1-z])f[tt[x][1-z]]=y;f[x]=f[y];if(f[y])tt[f[y]][son(y)]=x;f[y]=x;tt[x][1-z]=y;update(y);update(x);
}

其实代码很短(f[x]表示x的父亲,tt[x][0]和tt[x][1]分别是x的左右儿子)。
函数son(x)的作用是分辨x是其父亲的左儿子还是右儿子(左儿子是0,右儿子是1)。
当把x旋转到y是,y就成为的x的右儿子,但是x原来的右儿子B就没有地方放了。怎么办?我们发现在原序列中的顺序是X < B < Y, 所以B应该在X的右边但是在Y的左边,所以现在Y的左儿子应该是B右儿子不变。如图所示。
代码运用了一个小技巧z和1-z刚好把0和1转化,也可以打成z和z1(是xor,异或)。

将x节点旋转为y节点的儿子

void splay(int x,int y){if(x==y)return;remove(x,y);while(f[x]!=y){if(f[f[x]]!=y)if(son(f[x])==son(x))rotate(f[x]);else rotate(x);rotate(x);}if(!y)root=x;
}

remove是懒标记下传,后面再讲。
为什么是把x旋转为y的儿子,因为这样更加方便的操作,比如说要对x的子树进行操作,如果变成把x旋转到y的位置会麻烦很多而且不方便打。
旋转的思路:如果x和x的父亲和x的爷爷是一条折线,那么就旋转成不是一个折线,然后像上面将的旋转一样向上推进。具体的最好自己画个图,有利于理解。
一般像splay(x,0)就是把x旋转为根节点,splay(x,y)就是把x旋转为y的儿子(具体是左儿子还是有儿子根据原序列中的顺序来定)

节点值的更新

void update(int x){if(!x)return;t[x].size=1+t[tt[x][0]].size+t[tt[x][1]].size;t[x].sum=key[x]+t[tt[x][0]].sum+t[tt[x][1]].sum;t[x].lda=max(t[tt[x][0]].lda,t[tt[x][0]].sum+key[x]+t[tt[x][1]].lda);t[x].rda=max(t[tt[x][1]].rda,t[tt[x][1]].sum+key[x]+t[tt[x][0]].rda);t[x].mx=max(max(t[tt[x][0]].mx,t[tt[x][1]].mx),t[tt[x][0]].rda+t[tt[x][1]].lda+key[x]);}

当x节点的子节点变动是就需要更新,具体更新的内容据题目而定。

对于一段节点进行操作

x=kth(root,l-1);splay(x,0);
y=kth(root,r+1);splay(y,x);

如果要对[l,r]这段区间进行操作。思路:先把这段区间同时放到一颗子树上去且这可子树没有其他多余的节点。
首先如果把l-1旋转成根节点,那么[l,n]的节点都会在l-1(及root)的右子树上。然后再把r+1旋转为l-1的儿子,因为r+1在序列中再l-1的右边,所以r+1旋转之后一定是l-1的右儿子,那么在原序列中的顺序大于l-1的,小于r+1一定都是现在r+1节点的左子树了。
那么现在只要对r+1的左子树进行操作就好了。

     printf("%d\n",t[tt[y][0]].mx);

比如要输出[l,r]段的最大值,上段程序后面只用加上这段程序就可以了。

插入一个或者一段节点

现在要把a数组中的数从posi位置后开始插入进序列中。
参照对于一段节点进行操作

     fo(i,1,k)scanf("%d",&a[i]);x=kth(root,posi);splay(x,0);y=kth(root,posi+1);splay(y,x);tt[y][0]=build(1,k,y);

现在只需要把这k个数插进y的左子树中就可以了。build就是上面构图中的build,实质就是把a数组1到k中的节点插为y的子树。

删除一个或者一段节点

现在要从posi这个位置开始删去k个节点。
参照对于一段节点进行操作

    scanf("%d%d",&posi,&k);posi++;x=kth(root,posi-1);splay(x,0);y=kth(root,posi+k);splay(y,x);del(tt[y][0]);tt[y][0]=0;update(y);update(x);

这里也同理,因为要从posi开始删节点,序列的位置要比posi-1大,比posi+k小。
del函数是什么呢?

void del(int x){if(!x)return;shan[++shan[0]]=x;del(tt[x][0]);del(tt[x][1]);
}

因为删去了一些点,这些点原来的位置不能浪费在那里,用一个栈存起来,建点的之后再用。

建点操作

int insert(int x){int o;if(shan[0])o=shan[shan[0]--];else o=++num;//主要是这行初始化.例如:key[o]=t[o].sum=t[o].mx=x;t[o].size=1;根据题目而定..
}

为了防止空间的浪费,如果还有删除过得节点的位置空在那里的话,就调用出来,否则就新建一个位置。

区间的修改操作

例如从posi开始后的k个点都加上k,参照对于一段节点进行操作

x=kth(root,posi-1);splay(x,0);
y=kth(root,posi+k);splay(y,x);
change(tt[y][0],zhi);
update(y);update(x);

同理。

void change(int x,int y){//这里打的是区间加操作,据题目而定if(!x)return;t[x].sum+=t[x].size*y;key[x]+=y;t[x].add+=y;//add是懒标记,用于标记下传if(y>0)t[x].lda=t[x].rda=t[x].mx=t[x].sum;//这里是某道题的修改,据题目而定else t[x].lda=t[x].rda=0,t[x].mx=y;    
}

懒标记下传

void down(int x){if(!x)return;if(t[x].add!=maxn){change(tt[x][0],t[x].add);change(tt[x][1],t[x].add);t[x].add=maxn;}
}
void remove(int x,int y){do{d[++d[0]]=x;x=f[x];}while(x!=y);while(d[0])down(d[d[0]--]);
}

这种东西支持区间修改的数据结构都要用到的,但是splay中的有所不同,因为只有在旋转的之后才用的到,例如splay(x,y),所以需要把x到y的路径上的标记都下传。

区间翻转操作

例如把x的子树的区间翻转。

void overturn(int x){if(!x)return;swap(tt[x][0],tt[x][1]);t[x].biao^=1;
}

其实很简单,只需要把所有节点的左右儿子调换即可。注意懒标记的biao要用^或者1-biao,因为如果某段区间被同时翻转两次相当于没有翻转。

查询序列中第k个位置

int kth(int x,int k){down(x);if(t[tt[x][0]].size+1==k)return x;if(t[tt[x][0]].size+1>k)return kth(tt[x][0],k);else return kth(tt[x][1],k-t[tt[x][0]].size-1);
}

这个很简单啦。
其实如果想知道第x节点在序列中的序号的话,可以把x旋转到根(及splay(x,0)),然后t[tt[x][0]].size+1就是x在原序列中的序号。

区间分离

把x为根节点的这棵树以原序列序号y为分水岭,分成l和r两颗子树。

void split(int x,int y,int &l,int &r){int j=kth(x,y);splay(j,0);l=j,r=t[j][1];tt[l][1]=0;f[r]=0;update(j);
}

区间合并

把以x为根的树和以y为根的树合并为树l。
为什么要找到x树中第 size[x]大(及在原序列中序号最大的节点)的节点,因为在原序列中序号最大的节点没有右儿子,方便合并。

void merge(int x,int y,int &l){int j=kth(x,size[x]);splay(j,0);tt[j][1]=y;f[y]=j;update(j);l=j;
}

维护各种的树

比如说Link_Cut_Tree(及lct或动态树)…

由于本人是个蒟蒻

目前也只知道这么多了,但是这些操作在大部分题目中都够用了。

入门题

【CQOI2014】排序机械臂
【NOI2005】维护数列
什么最大值,排序也可以用splay来练练手。


http://chatgpt.dhexx.cn/article/HLjZkt4B.shtml

相关文章

Splay学习笔记

昨天这个时候到现在终于把Splay给搞明白了&#xff0c;还A了一道郁闷的出纳员&#xff1b;刚学完的感受&#xff1a;我再也不碰这东西了&#xff1b;做完郁闷的出纳员的感受&#xff1a;我发誓这辈子不当出纳员(虽然这确实只是个入门题……) 于是来讲一讲这个恶心的东西吧………

[Splay伸展树]splay树入门级教程

首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角。。 PS:若代码有误,请尽快与本人联系,我会尽快改正 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGang,不知道是不是因为和某位大牛…

Splay Tree(伸展树)

二叉查找树&#xff08;Binary Search Tree&#xff09;能够支持多种动态集合操作。因此&#xff0c;在信息学竞赛中&#xff0c;二叉排序树起着非常重要的作用&#xff0c;它可以被用来表示有序集合、建立索引或优先队列等。 作用于二叉查找树上的基本操作的时间是与树的高度…

浅谈splay

例题&#xff1a; 给出一个长度为n序列a。 有m次操作&#xff0c;每次操作可以修改a[i]&#xff0c;在第i个数前插入一个数x&#xff0c;或查询区间[l,r]的最大值。 1≤n≤100000,1≤m≤100000。 强制在线 看到这道题最自然的反应就是用线段树&#xff0c;但有了插入操作线段树…

关于splay的一些说明

前言splay出现的背景它的操作 push_uprotatesplayfindinsertnextdeletefindkthmain 前言 暑假快过完了,大家感觉是不是很棒!gay房也体验过最新的断电模拟器了. 好吧,在开讲之前我还是得说一个.刀剑神域的外传实在太好看了! 除了精彩的战斗场面,神一般的人设和强大的剧情,比…

Splay入门详解

Splay入门详解 写在前面 听说平衡树是一种强大的数据结构&#xff0c;听同年级或高年级大佬们讲起来也感觉很牛笔的亚子&#xff0c;而最近XC又叫我们去学习一下 L C T LCT LCT&#xff01;&#xff1f; 又因为 S p l a y Splay Splay是学习 L C T LCT LCT的基础&#xff0c;…

【学习笔记】Splay

普通平衡树 模板题链接 1、引入 一种二叉树&#xff0c;这棵树满足任意一个节点&#xff0c;它的左儿子的权值<自己的权值<右儿子的权值 这种树叫做二叉查找树&#xff0c;这个概念应该在初赛中见过了吧 Splay就是利用这个结构来实现的 2、变量 模板题的7大变量 s…

平衡树详解(Splay)

引入 先看例题&#xff1a;&#xff08;洛谷 P3369 【模板】普通平衡树&#xff09; 您需要写一种数据结构&#xff0c;来维护一些数&#xff0c;其中需要提供以下操作&#xff1a; 1.插入 x x x 数 2.删除 x x x 数(若有多个相同的数&#xff0c;因只删除一个) 3.查询 x x…

Splay入门解析【保证让你看不懂(滑稽)】

upd&#xff1a;忽然发现直接在百度上搜索 splay s p l a y 就会搜索到我这篇文章&#xff1f;小蒟蒻瑟瑟发抖。 因为CSDN这边我基本不会看回复什么的&#xff0c;所以如果有问题还是请移步cnblogs里面吧 Cnblogs里面这篇文章的链接 BST真是神奇的东西。。。 而且种类好多呀…

Splay Tree

类别&#xff1a;二叉排序树 空间效率&#xff1a;O(n) 时间效率&#xff1a;O(log n)内完成插入、查找、删除操作 创造者&#xff1a;Daniel Sleator和Robert Tarjan 优点&#xff1a;每次查询会调整树的结构&#xff0c;使被查询频率高的条目更靠近树根。 注&#xff1a;所有…

【算法详解】splay的初步了解

qwq表示最近感受到了不停课的鸭梨啊&#xff0c;好难搞啊……算法太难学了……我好菜啊qwq 其实半个多月前就可以写这篇文章&#xff0c;不过由于时间紧张没写emmmmmm&#xff0c;不扯闲言乱语了&#xff0c;如果有什么写的不好的地方凑活一下吧2333333333 splay是一种可以使树…

splay详解

一&#xff1a;什么是splay&#xff08;伸展树&#xff09; 首先了解二叉排序树 二叉排序树或者是一棵空树&#xff0c;或者是具有下列性质的 二叉树&#xff1a; &#xff08;1&#xff09;若左子树不空&#xff0c;则左子树上所有结点的值均小于或等于它的 根结点的值&#…

Splay 总结基础精华

前言&#xff1a; 第一次学习Splay是2月份&#xff0c;打板子的时候是3月份&#xff0c;Ac是4月份&#xff0c;写这篇博客是6月初&#xff1b;原因是因为我竟然发现我忘了Splay的板子了&#xff01;很慌&#xff0c;必须总结一下&#xff01; 不敢说是最详细的&#xff0c;但希…

关系型数据库与非关系型数据库详解

关系数据库与非关系型数据库 一、数据库概述1、关系型数据库2、非关系型数据库 二、数据库区别1、数据存储方式不同2、扩展方式不同3、对事务性的支持不同 三、非关系型数据库产生背景四、Redis简介1、Redis 优点 五、Redis 安装部署六、Redis 命令工具&#xff08;1&#xff0…

关系型数据库RDS基本简介

OSS存放非结构化的数据&#xff0c;如&#xff1a;音频、视频、图片 rds存放结构化的数据&#xff0c;如&#xff1a;一张表&#xff0c; RDS产品概要 可以根据业务的需求进行弹性伸缩。 采用主从备份架构&#xff0c;三节点数据存储&#xff0c;具备高可用性和数据可靠性 …

2 关系型数据库是什么?

目录结构 关系型数据库基本概念结构化查询语言数据定义语言&#xff08;DDL&#xff09;数据查询语言&#xff08;Data Query Language, DQL&#xff09;数据操纵语言&#xff08;Data Manipulation Language, DML&#xff09;数据控制语言&#xff08;Data Control Language, …

常见的关系型数据库有哪些

Oracle Database&#xff1a;由Oracle公司开发和维护的商业关系型数据库&#xff0c;具有广泛的应用场景和功能。 MySQL&#xff1a;开源关系型数据库&#xff0c;常用于Web应用程序和小型企业应用。 Microsoft SQL Server&#xff1a;由Microsoft公司开发和维护的商业关系型…

Bezier曲线快速相交计算(含代码)

Bezier曲线快速相交计算 背景介绍算法思路解释和分析示例参考资料 背景介绍 很多时候&#xff0c;需要计算曲线段与曲线段是否有交点。常规的思路是直接联立方程求解。不过&#xff0c;直接求方程的解这种思路通常在计算上开销较大。 针对任意曲线&#xff0c;曲线的方程阶次…

js绘制贝塞尔曲线(Bézier-Curve)

贝塞尔曲线(Bzier curve)&#xff0c;又称贝兹曲线或贝济埃曲线&#xff0c;是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线&#xff0c;贝兹曲线由线段与节点组成&#xff0c;节点是可拖动的支点&#xff0c;线段像可伸缩的皮筋&#xff0c;我们…

Bezier曲线与B-Spline曲线

微分几何基础 微分集合是用微分的方法来研究曲线的局部性质&#xff0c;如曲线的弯曲程度等。 一条可以用参数表示的三维曲线是一个有界点集&#xff0c;可以写成一个带参数的、连续的、单值的数学函数&#xff1a; { x x ( t ) y y ( t ) z z ( t ) 0 ⩽ t ⩽ 1 p p ( t…