学完Splay的查找作用,发现和普通的二叉查找树没什么区别,只是用了splay操作节省了时间开支。

而Splay序列之王的称号可不是白给的。

Splay真正强大的地方是他的区间操作。

怎么实现呢?

我们知道查找树的中序遍历是一个有序的序列。这个时候我们打破查找树左小右大的规则,而是把他的中序遍历作为我们的区间进行维护。

具体来讲有以下操作:

1、建树

2、区间操作【翻转、赋值啊什么的】

3、输出序列

建树

既然是区间,我们可以借鉴线段树的建树
void build(int& u,int l,int r,int fa)
{
if(l>=r) return;
u=++siz;
int mid=(l+r)>>1;
e[u].v=mid;
e[u].siz=1;
e[u].f=fa;
if(l+1<r)
{
build(e[u].ch[0],l,mid,u);
build(e[u].ch[1],mid+1,r,u);
e[u].siz+=e[e[u].ch[0]].siz+e[e[u].ch[1]].siz;
}
}
就用递归从中间建起

区间操作

我们想要从树中找到我们需要的区间,怎么办呢?
这个时候就是发挥Splay威力的时候了> <
Splay的伸展操作【也就是splay操作】可以在不改变中序遍历的前提下改变树的结构,也就是说我们可以在不改变序列的前提下改变树的形态。
那么这个时候,如果我们要操作区间[l,r]我们只需将l-1翻转到根节点,将r+1翻转到根节点的右儿子
这个时候想想,r+1的左子树是什么?
没错!就是我们要的区间[l,r]
怎么找到这两个节点?
就套用splay中的查找第k大节点就好了【其实在这里不能说是第k大,是第k个】
但是我们如果要操作[1,N]怎么办?
我们加一个0和N+1节点不就好啦【哨兵】
至于找到区间后怎么操作,就因题而异啦,一般都会加上一个lazy标记节省时间
附上splay操作
#define isr(x) (e[e[x].f].ch[1]==x)

...........

inline void spin(int u)
{
int fa=e[u].f,s=isr(u);
e[u].f=e[fa].f;
if(e[fa].f) e[e[fa].f].ch[isr(fa)]=u;
e[fa].ch[s]=e[u].ch[s^1];
e[fa].f=u;
if(e[u].ch[s^1]) e[e[u].ch[s^1]].f=fa;
e[u].ch[s^1]=fa;
up(fa);up(u);
} void splay(int u,int fa=0)
{
while(e[u].f!=fa)
{
if(e[e[u].f].f&&lazy[e[e[u].f].f]) pd(e[e[u].f].f);
if(lazy[e[u].f]) pd(e[u].f);
if(lazy[u]) pd(u);
if(e[e[u].f].f==fa) spin(u);
else if(isr(u)^isr(e[u].f)) spin(u),spin(u);
else spin(e[u].f),spin(u);
}
if(!fa) root=u;
}
其中lazy是打的标记,splay的时候记得下传【凡是涉及到儿子的操作都要考虑标记下传】
pd是标记下传函数

输出序列

其实就是中序遍历嘛,自己打【其实是我懒】
来道裸题:
洛谷P3391 文艺平衡树
裸的区间翻转
#include<iostream>
#include<cstdio>
#include<algorithm>
#define isr(x) (e[e[x].f].ch[1]==x)
using namespace std;
const int maxn=200005,INF=2000000000;
int N,M; inline int read()
{
int out=0,flag=1;char c=getchar();
while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}
while(c>=48&&c<=57){out=out*10+c-48;c=getchar();}
return out*flag;
} int lazy[maxn]; class node
{
public:
int v,ch[2],f,siz;
node() {ch[0]=ch[1]=0;siz=0;}
}e[maxn];
int siz=0,root=0; inline void up(int u){e[u].siz=e[e[u].ch[0]].siz+e[e[u].ch[1]].siz+1;} inline void pd(int u)
{
swap(e[u].ch[0],e[u].ch[1]);
lazy[e[u].ch[0]]^=1;
lazy[e[u].ch[1]]^=1;
lazy[u]^=1;
} inline void spin(int u)
{
int fa=e[u].f,s=isr(u);
e[u].f=e[fa].f;
if(e[fa].f) e[e[fa].f].ch[isr(fa)]=u;
e[fa].ch[s]=e[u].ch[s^1];
e[fa].f=u;
if(e[u].ch[s^1]) e[e[u].ch[s^1]].f=fa;
e[u].ch[s^1]=fa;
up(fa);up(u);
} void splay(int u,int fa=0)
{
while(e[u].f!=fa)
{
if(e[e[u].f].f&&lazy[e[e[u].f].f]) pd(e[e[u].f].f);
if(lazy[e[u].f]) pd(e[u].f);
if(lazy[u]) pd(u);
if(e[e[u].f].f==fa) spin(u);
else if(isr(u)^isr(e[u].f)) spin(u),spin(u);
else spin(e[u].f),spin(u);
}
if(!fa) root=u;
} /*void insert(int& u,int v,int fa)
{
if(!u) {e[u=++siz].v=v;e[u].f=fa;splay(u);}
else if(e[u].v>v) insert(e[u].ch[0],v,u);
else insert(e[u].ch[1],v,u);
}*/ int kth(int u,int k)
{
if(lazy[u]) pd(u);
if(k==e[e[u].ch[0]].siz+1) {/*splay(u);*/return u;}
else if(k>e[e[u].ch[0]].siz+1) return kth(e[u].ch[1],k-e[e[u].ch[0]].siz-1);
return kth(e[u].ch[0],k);
} void change(int l,int r)
{
int L=kth(root,l),R=kth(root,r+2);
splay(L);
splay(R,root);
lazy[e[e[root].ch[1]].ch[0]]^=1;
} void build(int& u,int l,int r,int fa)
{
if(l>=r) return;
u=++siz;
int mid=(l+r)>>1;
e[u].v=mid;
e[u].siz=1;
e[u].f=fa;
if(l+1<r)
{
build(e[u].ch[0],l,mid,u);
build(e[u].ch[1],mid+1,r,u);
e[u].siz+=e[e[u].ch[0]].siz+e[e[u].ch[1]].siz;
}
} void print(int u)
{
if(u)
{
if(lazy[u]) pd(u);
print(e[u].ch[0]);
if(e[u].v>=1&&e[u].v<=N) printf("%d ",e[u].v);
print(e[u].ch[1]);
}
} int main()
{
N=read();
M=read();
int a,b;
build(root,0,N+2,0);
while(M--)
{
a=read();
b=read();
if(a==b) continue;
change(a,b);
/*print(root);
cout<<endl;*/
}
print(root);
cout<<endl;
//system("pause >nul");
return 0;
}

Splay 的区间操作的更多相关文章

  1. [AHOI2006]文本编辑器 Splay tree区间操作

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1269 Description 这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个 ...

  2. 文艺平衡树-splay的区间操作

    真的是个神题,蒟蒻表示无力吐槽.刚开始以为是一个板子题,看着题解打了一遍,大概也理解了他是怎么实现的,然后我就去做别的题了,然后就在Three_D大佬的询问下蒙*了.最后还是问的nc哥,并思考了一个中 ...

  3. HDU 1754 I Hate It (Splay 区间操作)

    题目大意 维护一个序列,支持两种操作 操作一:将第x个元素的值修改为y 操作二:询问区间[x,y]内的元素的最大值 解题分析 splay的区间操作,事先加入两个编号最小和最大的点防止操作越界. 具体的 ...

  4. P2042 [NOI2005]维护数列 && Splay区间操作(四)

    到这里 \(A\) 了这题, \(Splay\) 就能算入好门了吧. 今天是个特殊的日子, \(NOI\) 出成绩, 大佬 \(Cu\) 不敢相信这一切这么快, 一下子机房就只剩我和 \(zrs\) ...

  5. P2596 [ZJOI2006]书架 && Splay 区间操作(三)

    P2596 [ZJOI2006]书架 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书, ...

  6. Splay 区间操作

    据大佬说,\(Splay\)是序列操作之王.\(Splay\)是一种平衡树,通过伸展(\(Splay\)),在不改变中序遍历的前提下变换根的位置,从而快速的进行序列操作 \(Splay\)最常见的序列 ...

  7. POJ-3468 A Simple Problem with Integers Splay Tree区间练习

    题目链接:http://poj.org/problem?id=3468 以前用线段树做过,现在用Splay Tree A了,向HH.kuangbin.cxlove大牛学习了各种Splay各种操作,,, ...

  8. Splay树简单操作

    前几天刚刚自学了一下splay,发现思路真简单实现起来好麻烦 先贴一下头文件 # include <stdio.h> # include <stdlib.h> # includ ...

  9. splay tree旋转操作 hdu 1890

    很神奇的旋转操作. 目前没看到其他数据结构能实现这个功能.平衡树不好处理区间操作,线段树很难旋转.splay tree搞这个就很简单了. 下面用的这个模板跑了700ms,好慢,估计是删除操作太费时了, ...

随机推荐

  1. pg执行计划

  2. js中对象转化成字符串、数字或布尔值的转化规则

    js中对象可以转化成 字符串.数字.布尔值 一.对象转化成字符串: 规则: 1.如果对象有toString方法,则调用该方法,并返回相应的结果:(代码通常会执行到这,因为在所有对象中都有toStrin ...

  3. 自动化运维工具saltstack02 -- 之SaltStack的配置管理

    SaltStack的配置管理 1.配置管理说明 配置管理,顾名思义及配置与管理, salt-master的配置文件编写格式之YAML语法说明: 数据的结构通过缩进来表示,每一级用两个空格来表示缩进,如 ...

  4. [线性DP][codeforces-1110D.Jongmah]一道花里胡哨的DP题

    题目来源: Codeforces - 1110D 题意:你有n张牌(1,2,3,...,m)你要尽可能多的打出[x,x+1,x+2] 或者[x,x,x]的牌型,问最多能打出多少种牌 思路: 1.三组[ ...

  5. 如何计算FOB价格

    FOB价格是国际贸易术语常有的一种算法,针对不同的对象,FOB价格也有不一样的算法.对于做外贸生意的朋友,需要了解FOB价格以及各项费用名称,以及如何计算FOB价格. FOB价格是国际FOB价格语常有 ...

  6. 2017软工 — 每周PSP

    1. PSP表格 2. PSP饼图 3. 本周进度条 4. 累计折线图

  7. lintcode-81-数据流中位数

    81-数据流中位数 数字是不断进入数组的,在每次添加一个新的数进入数组的同时返回当前新数组的中位数. 说明 中位数的定义: 中位数是排序后数组的中间值,如果有数组中有n个数,则中位数为A[(n-1)/ ...

  8. Spark Shuffle之Sort Shuffle

    源文件放在github,随着理解的深入,不断更新,如有谬误之处,欢迎指正.原文链接https://github.com/jacksu/utils4s/blob/master/spark-knowled ...

  9. 使用windows live writer发表的博客

    试插入代码 #include <iostream.h> using namespace std; int main() { cout<<"hello world&qu ...

  10. 此时本机的BootLoader程序坏了,也就是说grub第一阶段坏掉了,该如何修复

    方法一:直接安装grub (1)先把MBR拷贝一份 dd if=/dev/sda of=/tmp/mbr count=1 bs=512   (2)然后再破坏 dd if=/dev/zero of=/d ...