初识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. python装饰器的参数传递

    被装饰器装饰的函数名即使没有被调用(因为有@xxx,会触发运行装饰器),(装饰器工厂函数)定义装饰器的代码已经运行了(最内部的那个函数并没有运行)(把被装饰的原函数引用赋值给了装饰器内部的那个函数名) ...

  2. vim编辑模式下黑色背景,下来过程中出现白条的问题

    问题描述,原本是黑色背景,但是下拉过程中没有文字的地方会变成白色,非常影响美观,搞了好久现在中与改好了.问题如图: 打开-/.vimrc 文件,在如图所示位置加上62-64行代码即可.root用户的添 ...

  3. vue-cli3解决跨域问题

    在 vue.config.js里面配置 devServer: { proxy: { '/': { // search为转发路径 target: 'http://www.baidu.com', // 目 ...

  4. 结构体初始化和new delete

    int *p; p=new int[100]; delete []p; 结构体中的指针需要初始化

  5. Overlapping generations model

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

  6. Android之布局RelativeLayout

    线性布局的weight属性在等比例分配时比较方便,但是对复杂的界面,嵌套多层LinearLayout布局会导致渲染变慢,占用更多系统资源:而使用RelativeLayout的话,可能仅仅需要一层就可以 ...

  7. 9.数据分组 group by

    --数据分组 group by --作用:用于 对查询的数据进行分组,并处理 select deptno,job from emp group by deptno,job --1.分组之后,不能将除分 ...

  8. C# 元组

    Tuple<,); Console.WriteLine(t.Item1); Console.WriteLine(t.Item2); C#7 可以使用圆括号声明一个元组: (); Console. ...

  9. AdminSwagger2Configuration

    package org.linlinjava.litemall.admin.config; import org.springframework.context.annotation.Bean; im ...

  10. LeetCode No.91,92,93

    No.91 NumDecodings 解码方法 题目 一条包含字母 A-Z 的消息通过以下方式进行了编码: 'A' -> 1 'B' -> 2 ... 'Z' -> 26 给定一个只 ...