第二棵树:Splay
Splay这东西神难打……什么都没动板子敲上就直逼200行了,而且非常难记(仿佛是模板长的必然结果)。但是为什么还要学呢?据说是因为它可以实现区间操作。但是自从我得知无旋Treap也能做到这些,默默对比了一下代码长度之后分分钟抛弃Splay啊= =。
和Treap用随机值和左右旋维护平衡不同的,Splay用它的核心操作Splay来维护平衡。所谓的Splay操作可以把任何一个节点旋转到它的一个祖先节点,而旋转分单旋和双旋,双旋需要对比它与父亲是否在各自父亲的同侧。然后每次需要打标记移区间删树之类的,它居然要把目标区间的两端分别移到根和根的儿子……极其麻烦啊这个东西。放几道例题,再体会吧。见到Splay,才知Treap好。
普通平衡树[Tyvj 1728]
时间限制:1 s 内存限制:128 MB
【题目描述】
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
【输入格式】
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
【输出格式】
对于操作3,4,5,6每行输出一个数,表示对应答案
【样例输入】
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
【样例输出】
106465
84185
492737
【提示】
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int sj=;
int ch[sj][],f[sj],size[sj],cnt[sj],key[sj];
int sz,root;
inline void clear(int x)
{
ch[x][]=ch[x][]=f[x]=size[x]=cnt[x]=key[x]=;
}
inline bool get(int x)
{
return ch[f[x]][]==x;
}
inline void update(int x)
{
if(x)
{
size[x]=cnt[x];
if(ch[x][]) size[x]+=size[ch[x][]];
if(ch[x][]) size[x]+=size[ch[x][]];
}
}
inline void rotate(int x)
{
int old=f[x],oldf=f[old],whichx=get(x);
ch[old][whichx]=ch[x][whichx^];
f[ch[old][whichx]]=old;
ch[x][whichx^]=old;
f[old]=x;
f[x]=oldf;
if(oldf) ch[oldf][ch[oldf][]==old]=x;
update(old);
update(x);
}
inline void splay(int x)
{
for(int fa;fa=f[x];rotate(x))
if(f[fa]) rotate(get(x)==get(fa)?fa:x);
root=x;
}
inline void insert(int x)
{
if(root==)
{
sz++;
ch[sz][]=ch[sz][]=f[sz]=;
root=sz;
size[sz]=cnt[sz]=;
key[sz]=x;
return;
}
int now=root,fa=;
while()
{
if(x==key[now])
{
cnt[now]++;
update(now);
update(fa);
splay(now);
break;
}
fa=now;
now=ch[now][key[now]<x];
if(now==)
{
sz++;
ch[sz][]=ch[sz][]=;
f[sz]=fa;
size[sz]=cnt[sz]=;
ch[fa][key[fa]<x]=sz;
key[sz]=x;
update(fa);
splay(sz);
break;
}
}
}
inline int find(int x)
{
int now=root,ans=;
while()
{
if(x<key[now]) now=ch[now][];
else
{
ans+=(ch[now][]?size[ch[now][]]:);
if(x==key[now])
{
splay(now);
return ans+;
}
ans+=cnt[now];
now=ch[now][];
}
}
}
inline int findx(int x)
{
int now=root;
while()
{
if(ch[now][]&&x<=size[ch[now][]])
now=ch[now][];
else
{
int temp=(ch[now][]?size[ch[now][]]:)+cnt[now];
if(x<=temp) return key[now];
x-=temp;
now=ch[now][];
}
}
}
inline int pre()
{
int now=ch[root][];
while(ch[now][]) now=ch[now][];
return now;
}
inline int next()
{
int now=ch[root][];
while(ch[now][]) now=ch[now][];
return now;
}
inline void del(int x)
{
int whatever=find(x);
if(cnt[root]>)
{
cnt[root]--;
update(root);
return;
}
if(!ch[root][]&&!ch[root][])
{
clear(root);
root=;
return;
}
if(!ch[root][])
{
int oldroot=root;
root=ch[root][];
f[root]=;
clear(oldroot);
return;
}
else if(!ch[root][])
{
int oldroot=root;
root=ch[root][];
f[root]=;
clear(oldroot);
return;
}
int leftbig=pre(),oldroot=root;
splay(leftbig);
ch[root][]=ch[oldroot][];
f[ch[oldroot][]]=root;
clear(oldroot);
update(root);
}
int main()
{
int n,opt,x;
scanf("%d",&n);
for(int i=;i<=n;i++)
{
scanf("%d%d",&opt,&x);
if(opt==) insert(x);
if(opt==) del(x);
if(opt==) printf("%d\n",find(x));
if(opt==) printf("%d\n",findx(x));
if(opt==)
{
insert(x);
printf("%d\n",key[pre()]);
del(x);
}
if(opt==)
{
insert(x);
printf("%d\n",key[next()]);
del(x);
}
}
return ;
}
Splay数组实现【基本操作】
[HZOI 2016][Tyvj 1729]文艺平衡树
时间限制:1 s 内存限制:128 MB
【题目描述】
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
【输入格式】
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
【输出格式】
输出一行n个数字,表示原始序列经过m次变换后的结果
【样例输入】
5 3
1 3
1 3
1 4
【样例输出】
4 3 2 1 5
【数据范围】
N,M<=100000
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int siz,sum,flag;
node *ch[],*fa;
void pushdown(node *nd)
{
if(flag)
{
swap(ch[],ch[]);
if(ch[]!=nd) ch[]->flag^=;
if(ch[]!=nd) ch[]->flag^=;
flag=;
}
}
void update()
{
siz=ch[]->siz+ch[]->siz+;
}
}c[],*tail=c,*root,*null;
int n,m,a1,a2;
void init()
{
null=++tail;
null->siz=;
null->ch[]=null->ch[]=null;
null->sum=null->flag=;
}
node* newnode(node *fa)
{
node *nd=++tail;
nd->fa=fa;
nd->siz=;
nd->ch[]=nd->ch[]=null;
nd->flag=;
return nd;
}
void rot(node*& x,int d)
{
node* y=x->fa;
y->ch[!d]=x->ch[d];
if(x->ch[d]!=null) x->ch[d]->fa=y;
x->fa=y->fa;
if(y->fa!=null)
(y==y->fa->ch[])?y->fa->ch[]=x:y->fa->ch[]=x;
x->ch[d]=y;
y->fa=x;
x->update();
y->update();
}
node *build(node *fa,int lf,int rg)
{
if(lf>rg) return null;
node *nd=newnode(fa);
if(lf==rg)
{
nd->sum=lf;
return nd;
}
int mid=(lf+rg)>>;
nd->sum=mid;
nd->ch[]=build(nd,lf,mid-);
nd->ch[]=build(nd,mid+,rg);
nd->update();
return nd;
}
void splay(node *nd,node *tar)
{
while(nd->fa!=tar)
{
node *ne=nd->fa;
if(nd==ne->ch[])
{
if(ne->fa!=tar&&ne==ne->fa->ch[])
rot(ne,);
rot(nd,);
}
else
{
if(ne->fa!=tar&&ne==ne->fa->ch[])
rot(ne,);
rot(nd,);
}
}
if(tar==null) root=nd;
}
node *kth(node *nd,int k)
{
nd->pushdown(null);
if(nd->ch[]->siz+==k) return nd;
if(nd->ch[]->siz+>k) return kth(nd->ch[],k);
else return kth(nd->ch[],k-nd->ch[]->siz-);
}
void rev(int l,int r)
{
node *x=kth(root,l);
node *y=kth(root,r+);
splay(x,null);
splay(y,root);
y->ch[]->flag^=;
}
void dfs(node *nd)
{
if(nd==null) return;
nd->pushdown(null);
dfs(nd->ch[]);
if(nd->sum>=&&nd->sum<=n)
printf("%d ",nd->sum);
dfs(nd->ch[]);
}
int main()
{
scanf("%d%d",&n,&m);
init();
root=build(null,,n+);
for(int i=;i<=m;i++)
{
scanf("%d%d",&a1,&a2);
rev(a1,a2);
}
dfs(root);
return ;
}
Splay指针实现【区间翻转】
Description
Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1, A2, ... An}. Then the host performs a series of operations and queries on the sequence which consists:
- ADD x y D: Add D to each number in sub-sequence {Ax ... Ay}. For example, performing "ADD 2 4 1" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5, 5}
- REVERSE x y: reverse the sub-sequence {Ax ... Ay}. For example, performing "REVERSE 2 4" on {1, 2, 3, 4, 5} results in {1, 4, 3, 2, 5}
- REVOLVE x y T: rotate sub-sequence {Ax ... Ay} T times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
- INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5}
- DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
- MIN x y: query the participant what is the minimum number in sub-sequence {Ax ... Ay}. For example, the correct answer to "MIN 2 4" on {1, 2, 3, 4, 5} is 2
To make the show more interesting, the participant is granted a chance to turn to someone else that means when Jackson feels difficult in answering a query he may call you for help. You task is to watch the TV show and write a program giving the correct answer to each query in order to assist Jackson whenever he calls.
Input
The first line contains n (n ≤ 100000).
The following n lines describe the sequence.
Then follows M (M ≤ 100000), the numbers of operations and queries.
The following M lines describe the operations and queries.
Output
For each "MIN" query, output the correct answer.
Sample Input
5
1
2
3
4
5
2
ADD 2 4 1
MIN 4 5
Sample Output
5
Source
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ky ch[ch[root][1]][0]
using namespace std;
const int sj=;
const int wq=0x3f3f3f3f;
int n,a[sj],a1,a2,a3,m,rev[sj],mi[sj],add[sj],s[sj];
char ss[];
int pre[sj],ch[sj][],root,tot1,size[sj],key[sj],tot2;
void newnode(int &r,int father,int k)
{
if(tot2) r=s[tot2--];
else r=++tot1;
pre[r]=father;
ch[r][]=ch[r][]=;
key[r]=k;
mi[r]=k;
rev[r]=add[r]=;
size[r]=;
}
void update_rev(int r)
{
if(!r) return;
swap(ch[r][],ch[r][]);
rev[r]^=;
}
void update_add(int r,int d)
{
if(!r) return;
mi[r]+=d;
key[r]+=d;
add[r]+=d;
}
void push_up(int r)
{
size[r]=size[ch[r][]]+size[ch[r][]]+;
mi[r]=min(key[r],min(mi[ch[r][]],mi[ch[r][]]));
}
void push_down(int r)
{
if(rev[r])
{
update_rev(ch[r][]);
update_rev(ch[r][]);
rev[r]=;
}
if(add[r])
{
update_add(ch[r][],add[r]);
update_add(ch[r][],add[r]);
add[r]=;
}
}
void build(int &x,int l,int r,int father)
{
if(l>r) return;
int mid=(l+r)>>;
newnode(x,father,a[mid]);
build(ch[x][],l,mid-,x);
build(ch[x][],mid+,r,x);
push_up(x);
}
void rotate(int x,int kind)
{
int y=pre[x];
push_down(y);
push_down(x);
ch[y][!kind]=ch[x][kind];
pre[ch[x][kind]]=y;
if(pre[y]) ch[pre[y]][ch[pre[y]][]==y]=x;
pre[x]=pre[y];
ch[x][kind]=y;
pre[y]=x;
push_up(y);
}
void splay(int r,int goal)
{
push_down(r);
while(pre[r]!=goal)
{
if(pre[pre[r]]==goal)
{
push_down(pre[r]);
push_down(r);
rotate(r,ch[pre[r]][]==r);
}
else
{
push_down(pre[pre[r]]);
push_down(pre[r]);
push_down(r);
int y=pre[r];
int kind=ch[pre[y]][]==y;
if(ch[y][kind]==r)
{
rotate(r,!kind);
rotate(r,kind);
}
else
{
rotate(y,kind);
rotate(r,kind);
}
}
}
push_up(r);
if(goal==) root=r;
}
int get_kth(int r,int k)
{
push_down(r);
int t=size[ch[r][]]+;
if(t==k) return r;
if(t>k) return get_kth(ch[r][],k);
else return get_kth(ch[r][],k-t);
}
void Add(int x,int y,int d)
{
splay(get_kth(root,x),);
splay(get_kth(root,y+),root);
update_add(ky,d);
push_up(ch[root][]);
push_up(root);
}
void reverse(int x,int y)
{
splay(get_kth(root,x),);
splay(get_kth(root,y+),root);
update_rev(ky);
push_up(ch[root][]);
push_up(root);
}
void revolve(int x,int y,int t)
{
int len=y-x+;
t=(t%len+len)%len;
splay(get_kth(root,y-t+),);
splay(get_kth(root,y+),root);
int tmp=ky;
ky=;
push_up(ch[root][]);
push_up(root);
splay(get_kth(root,x),);
splay(get_kth(root,x+),root);
ky=tmp;
pre[tmp]=ch[root][];
push_up(ch[root][]);
push_up(root);
}
void insert(int x,int p)
{
splay(get_kth(root,x+),);
splay(get_kth(root,x+),root);
newnode(ky,ch[root][],p);
push_up(ch[root][]);
push_up(root);
}
void erase(int r)
{
if(!r) return;
s[++tot2]=r;
erase(ch[r][]);
erase(ch[r][]);
}
void Delete(int x)
{
splay(get_kth(root,x),);
splay(get_kth(root,x+),root);
erase(ky);
pre[ky]=;
ky=;
push_up(ch[root][]);
push_up(root);
}
int Min(int x,int y)
{
splay(get_kth(root,x),);
splay(get_kth(root,y+),root);
return mi[ky];
}
int main()
{
scanf("%d",&n);
for(int i=;i<n;i++) scanf("%d",&a[i]);
mi[root]=wq;
newnode(root,,-);
newnode(ch[root][],root,-);
build(ky,,n-,ch[root][]);
push_up(ch[root][]);
push_up(root);
scanf("%d",&m);
for(int i=;i<=m;i++)
{
scanf("%s%d",ss,&a1);
if(ss[]=='A')
scanf("%d%d",&a2,&a3),Add(a1,a2,a3);
if(ss[]=='R')
{
scanf("%d",&a2);
if(ss[]=='E')
reverse(a1,a2);
if(ss[]=='O')
scanf("%d",&a3),revolve(a1,a2,a3);
}
if(ss[]=='I')
scanf("%d",&a2),insert(a1,a2);
if(ss[]=='D')
Delete(a1);
if(ss[]=='M')
scanf("%d",&a2),printf("%d\n",Min(a1,a2));
}
return ;
}
Splay数组实现【多种区间操作】
为天地立心,为生民请命,为往圣继绝学,为万世开太平。
第二棵树:Splay的更多相关文章
- hdu 2871 Memory Control(伸展树splay tree)
hdu 2871 Memory Control 题意:就是对一个区间的四种操作,NEW x,占据最左边的连续的x个单元,Free x 把x单元所占的连续区间清空 , Get x 把第x次占据的区间输出 ...
- 伸展树Splay【非指针版】
·伸展树有以下基本操作(基于一道强大模板题:codevs维护队列): a[]读入的数组;id[]表示当前数组中的元素在树中节点的临时标号;fa[]当前节点的父节点的编号;c[][]类似于Trie,就是 ...
- 伸展树 Splay Tree
Splay Tree 是二叉查找树的一种,它与平衡二叉树.红黑树不同的是,Splay Tree从不强制地保持自身的平衡,每当查找到某个节点n的时候,在返回节点n的同时,Splay Tree会将节点n旋 ...
- 伸展树 Splay 模板
学习Splay的时候参考了很多不同的资料,然而参考资料太杂的后果就是模板调出来一直都有问题,尤其是最后发现网上找的各种资料均有不同程度的错误. 好在啃了几天之后终于算是啃下来了. Splay也算是平衡 ...
- [Splay伸展树]splay树入门级教程
首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...
- bzoj 2816: [ZJOI2012]网络 (LCT 建多棵树)
链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2816 题面: http://www.lydsy.com/JudgeOnline/upload ...
- K:伸展树(splay tree)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...
- 判断一棵树是否为二叉搜索树(二叉排序树) python
输入一棵树,判断这棵树是否为二叉搜索树.首先要知道什么是排序二叉树,二叉排序树是这样定义的,二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上所有结点的值均小于它的 ...
- 【BZOJ4928】第二题 树hash+倍增
[BZOJ4928]第二题 Description 对于一棵有根树,定义一个点u的k-子树为u的子树中距离u不超过k的部分. 注意,假如u的子树中不存在距离u为k的点,则u的k-子树是不存在的. 定义 ...
随机推荐
- Java代码实现 增删查 + 分页——实习第四天
今天项目内容已经开始了,并且已经完成好多基本操作,今天就开始总结今天学习到的内容,和我遇到的问题,以及分析这其中的原因. 内容模块: 1:Java代码实现对数据库的增删查: 2:分页且获取页面信息: ...
- 身份证识别OCR,开启视频模式扫一扫即可识别身份证信息
文章摘要:身份证识别等证件识别OCR技术在各个行业得到广泛应用,例如:车险移动查勘会用到身份证识别.行驶证识别.车架号识别: 寿险移动展业会用到名片识别.银行卡识别:电信实名制代理网点采集身份证信息会 ...
- RabbitMQ安装|使用|概念|Golang开发
搬砖的陈大师版权所有,转载请注明:http://www.lenggirl.com/tool/RabbitMQ.html 手册:http://www.rabbitmq.com/getstarted.ht ...
- ReactiveSwift源码解析(七) Signal的CombineLatest的代码实现
本篇博客我们就来聊一下combineLatest()的使用以及具体的实现方式.在之前的<iOS开发之ReactiveCocoa下的MVVM>的博客中我们已经聊过combineLatest( ...
- 利用Python实现一个感知机学习算法
本文主要参考英文教材Python Machine Learning第二章.pdf文档下载链接: https://pan.baidu.com/s/1nuS07Qp 密码: gcb9. 本文主要内容包括利 ...
- HttpClient以json形式的参数调用http接口并对返回的json数据进行处理(可以带文件)
1.参数的url就是被调用的地址,map是你要传的参数.参数转成json我使用的是gson方式转换的. 主要使用的jar包有httpclient-4.5.3.jar.httpcore-4.4.6.ja ...
- JavaScript一个cookie存储的类
所有输出都在浏览器的控制台中 <script type="text/javascript"> /** * cookieStorage.js * 本类实现像localSt ...
- hdu 2612 多终点BFS
Find a way Problem Description Pass a year learning in Hangzhou, yifenfei arrival hometown Ningbo at ...
- Es6 新增解构赋值
1.数组的解构赋值 基本用法 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring). 要想实现解构,就必须是容器,或者具有可遍历的接口. 以前,为 ...
- Android - TabHost 与 Fragment 制作页面切换效果
Android - TabHost 与 Fragment 制作页面切换效果 Android API 19 , API 23 三个标签页置于顶端 效果图: 在文件BoardTabHost.java中定义 ...