初识splay

学splay有一段时间了,一直没写......

本题是splay模板题,维护一个1~n的序列,支持区间翻转(比如1 2 3 4 5 6变成1 2 3 6 5 4),最后输出结果序列。

模板题嘛......主要了解一下splay的基本操作QwQ

原题地址链接[洛谷P3391文艺平衡树]

1.基本概念

splay是一种二叉搜索树,节点的权值满足lson<p<rson,故可以像其他二叉搜索树一样在树上二分查找某数排名,排名为k的数,以及前驱后继等。

普通的二叉搜索树在面对特殊数据时树的深度会从log n退化成接近n(退化成链),这样操作的时间复杂度会从O(log n)退化成O(n),影响效率。

splay通过旋转维持树的平衡。这个操作后面会提到。

2.基本操作

二叉搜索树的基本操作:求排名为k的数。

 int rank(int p,int k)
{
pushdown(p);
if(k<=sz[s[p][]])
return rank(s[p][],k);
else if(k==sz[s[p][]]+)
return p;
else
return rank(s[p][],k-sz[s[p][]]-);
}

简单的树上二分。

3.核心操作:splay

splay的精髓在于骚气的旋转。(名字就是这么来的哈哈哈~)

splay的核心操作是splay(一脸懵逼),splay(x,y)意为通过一系列旋转,将点x旋转到点y下面,使x成为y的儿子。

每次旋转通过rotate函数实现:

 void rotate(int p)
{
int fa=f[p];
bool k=id(p);
s[fa][k]=s[p][!k];
s[p][!k]=fa;
s[f[fa]][id(fa)]=p;
f[p]=f[fa];
f[s[fa][k]]=fa;
f[fa]=p;
refresh(fa);
refresh(p);
}

rotate的时候严格满足splay二叉搜索树的性质:lson<p<rson。

将p提到fa的位置,根据大小关系决定fa是作为p的左儿子还是右儿子,这样实际上是fa挤掉了p原先的某个儿子,而p转上去,让出了fa的一个儿子的位置。

所以最后让那个被fa挤掉的p的孤儿作为fa的某个儿子,填到空缺的地方去(原来p的位置)。

至于splay的实现方法...有两种:单旋和双旋。

单旋即无脑地一直转,直到把x转到y下面。

 void splay(int p,int g)  // 单旋
{
while(f[p]!=g)rotate(p);
if(!g)root=p;
}

比起单旋,双旋能更好的维护splay的平衡。

 void splay(int p,int g) // 双旋
{
while(f[p]!=g)
{
int fa=f[p];
if(f[fa]==g)
{
rotate(p);
break;
}
if(id(p)^id(fa))rotate(p);
else rotate(fa);
rotate(p);
}
if(!g)root=p;
}

利用splay操作,我们就可以用这棵树实现很多其它平衡树实现不了的功能。

4.元素的插入、删除、查询及修改

设x为 要插入的/要删除的/要查询的/要修改的 元素or区间。

进行这些操作之前,运用旋转操作把x的前驱pre转到根位置,把x的后继post转到根的下面,post>pre,所以此时post一定是pre的右儿子。

(如果是区间,pre就是left的前驱,post就是right的后继)

如图:

此时,根据二叉搜索树的性质,要删除/查询/修改的元素or区间就一定在post的左子树那里。如图:(目标子树:红色部分)

4.1 插入

如果是插入,红色部分一定为空,在那里插入即可。

4.2 删除

残忍抛弃红色部分。

4.3 查询

在红色部分查询。

4.4 修改

在这道题里是区间翻转。

我们并不需要真的翻转,打个标记就行。

标记需要下传的时候,交换左右子树的左右子树,在左右儿子上打标记,清掉自身标记。

 void pushdown(int p)
{
if(!fl[p])return;
fl[s[p][]]^=;
fl[s[p][]]^=;
swap(s[s[p][]][],s[s[p][]][]);
swap(s[s[p][]][],s[s[p][]][]);
fl[p]=;
}

这样就行了。

完事了?

完事了。

最后二分输出序列即可。

其他细节见代码。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define id(x) (s[f[x]][1]==x) // 判断是左儿子还是右儿子
using namespace std; int f[N],s[N][],val[N],sz[N],root,tot; // 分别是父亲,儿子,值,子树大小,树根,元素数量
bool fl[N]; // 翻转标记 void refresh(int p) // 更新size
{
sz[p]=sz[s[p][]]+sz[s[p][]]+;
} void pushdown(int p) // 下传标记
{
if(!fl[p])return;
fl[s[p][]]^=;
fl[s[p][]]^=;
swap(s[s[p][]][],s[s[p][]][]);
swap(s[s[p][]][],s[s[p][]][]);
fl[p]=;
} void rotate(int p) // 把p转上去
{
int fa=f[p];
bool k=id(p);
s[fa][k]=s[p][!k];
s[p][!k]=fa;
s[f[fa]][id(fa)]=p;
f[p]=f[fa];
f[s[fa][k]]=fa;
f[fa]=p;
refresh(fa);
refresh(p);
}
/*
void splay(int p,int g) // 单旋
{
while(f[p]!=g)rotate(p);
if(!g)root=p;
}
*/
void splay(int p,int g) // 双旋
{
while(f[p]!=g)
{
int fa=f[p];
if(f[fa]==g)
{
rotate(p);
break;
}
if(id(p)^id(fa))rotate(p);
else rotate(fa);
rotate(p);
}
if(!g)root=p;
} int rank(int p,int k) // 查询rank为k的元素
{
pushdown(p);
if(k<=sz[s[p][]])
return rank(s[p][],k);
else if(k==sz[s[p][]]+)
return p;
else
return rank(s[p][],k-sz[s[p][]]-);
} int build(int l,int r,int fa) // 建树 实际上一个一个插入也行,但是这样二分建树可以使初始树更平衡
{
if(l>r)return ;
int mid=(l+r)>>;
int p=++tot;
s[p][]=build(l,mid-,p);
s[p][]=build(mid+,r,p);
val[p]=mid;
f[p]=fa;
refresh(p);
return p;
} void change(int l,int r) // 区间翻转
{
int pre,post,rt;
pre=rank(root,l-);
splay(pre,);
post=rank(root,r+);
splay(post,pre);
rt=s[post][];
swap(s[rt][],s[rt][]);
fl[rt]^=;
} void print(int p) // 二分输出结果序列
{
if(!p)return;
pushdown(p);
print(s[p][]);
printf("%d ",val[p]);
print(s[p][]);
} int n,m; int main()
{
scanf("%d%d",&n,&m);
root=build(,n+,);
for(int i=;i<=m;i++)
{
int lb,rb;
scanf("%d%d",&lb,&rb);
change(lb+,rb+);
}
splay(rank(root,),);
splay(rank(root,n+),root);
print(s[s[root][]][]);
return ;
} complete code of splay tree

complete code of splay tree

[洛谷P3391] 文艺平衡树 (Splay模板)的更多相关文章

  1. 洛谷P3391文艺平衡树(Splay)

    题目传送门 转载自https://www.cnblogs.com/yousiki/p/6147455.html,转载请注明出处 经典引文 空间效率:O(n) 时间效率:O(log n)插入.查找.删除 ...

  2. BZOJ3223/洛谷P3391 - 文艺平衡树

    BZOJ链接 洛谷链接 题意 模板题啦~2 代码 //文艺平衡树 #include <cstdio> #include <algorithm> using namespace ...

  3. BZOJ3224/洛谷P3391 - 普通平衡树(Splay)

    BZOJ链接 洛谷链接 题意简述 模板题啦~ 代码 //普通平衡树(Splay) #include <cstdio> int const N=1e5+10; int rt,ndCnt; i ...

  4. 洛谷 P3391 文艺平衡树

    题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 --b ...

  5. 洛谷P3391 文艺平衡树 (Splay模板)

    模板题. 注意标记即可,另外,涉及区间翻转操作,记得设立首尾哨兵. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int ...

  6. 洛谷.3391.文艺平衡树(fhq Traep)

    题目链接 //注意反转时先分裂r,因为l,r是针对整棵树的排名 #include<cstdio> #include<cctype> #include<algorithm& ...

  7. 洛谷 P3391 【模板】文艺平衡树(Splay)

    题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1, ...

  8. 【阶梯报告】洛谷P3391【模板】文艺平衡树 splay

    [阶梯报告]洛谷P3391[模板]文艺平衡树 splay 题目链接在这里[链接](https://www.luogu.org/problemnew/show/P3391)最近在学习splay,终于做对 ...

  9. luoguP3391[模板]文艺平衡树(Splay) 题解

    链接一下题目:luoguP3391[模板]文艺平衡树(Splay) 平衡树解析 这里的Splay维护的显然不再是权值排序 现在按照的是序列中的编号排序(不过在这道题目里面就是权值诶...) 那么,继续 ...

随机推荐

  1. 探讨 Git 代码托管平台的若干问题 - 2019 版

    关于 Git 版本控制软件种类繁多,维基百科收录的最早的版本控制系统是 1972 年贝尔实验室开发的 Source Code Control System.1986 年 Concurrent Vers ...

  2. 编程作业3.1:Multi-class classification(One-vs-all)

    题目: 在本次练习中,你将使用逻辑回归和神经网络来识别手写数字(从0到9). 今天,自动手写数字识别被广泛使用,从识别信封上的邮政编码到识别银行支票上的金额.这个练习将向你展示如何将你所学的方法用于此 ...

  3. 套接字详解(socket)

    用户认为的信息之间传输只是建立以两个应用程序上,实际上在TCP连接中是靠套接字来作为他们连接的桥梁. 那么什么是套接字呢? TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做 ...

  4. SVN常见错误解决办法和批量add等命令

    批量添加所有更改文件 svn add . --no-ignore --force 提交文件 svn commit -m "up" File already exists: file ...

  5. docker 一些简略环境搭建及部分链接

    1.center 7  搭建 docker https://www.cnblogs.com/yufeng218/p/8370670.html 2.docker 命令 https://www.cnblo ...

  6. Ubuntu18.04 离线安装 docker

    Ubuntu18.04 离线安装 dockerhttps://blog.csdn.net/u012814856/article/details/804231851. 将下载下来的文件夹中的 4 个文件 ...

  7. RDD(五)——action

    reduce(func) 通过func函数聚集RDD中的所有元素并得到最终的结果,先聚合分区内数据,再聚合分区间数据.Func函数决定了聚合的方式. def main(args: Array[Stri ...

  8. 如何离开/退出/停用Python virtualenv

    我正在使用virtualenv和virtualenvwrapper. 我可以使用workon命令在virtualenv之间切换. me@mymachine:~$ workon env1 (env1)m ...

  9. Overlapping generations model

    I.6 Overlapping generations 世代被分离开,世代不重复一定满足哈代公式的条件,但是现实情况远没有这么简单(因为会世代重叠,即亲代死去同时一个亲代在不同时间都有可能产生子代,因 ...

  10. Java面试宝典2017

    JAVA面试.笔试题(2017版)                 欲想成功,必须用功!   目录 一.                  HTML&CSS部分................ ...