Uva 11922 Splay
Splay(伸展树)实现可分裂与合并的序列
对于BST,除了Treap树之外,还有一种Splay的伸展树,他能快速的分裂与合并。
重要的操作是伸展操作,将一个指定的结点 x 旋转到根的过程。
分三种情况,一次单旋,两次同向单旋,两次反向旋转。可以手动模拟一下这个过程。
到这里,问题常常是将序列的第 k 个元素旋转到根。
首先,要知道的是伸展树里面v存的是什么,是节点的编号(下标)。这样才能像 Treap实现名次树那样,很方便的找到左边第 k 个元素。
//将序列左数第k个元素选择到根
void splay(Node* &o,int k) {
o->pushdown();
int d = o->cmp(k);
if(d==1) k-=o->ch[0]->s + 1;
if(d!=-1) {
Node* p = o->ch[d];
p->pushdown();
int d2 = p->cmp(k);
int k2 = (d2==0 ? k : k - p->ch[0]->s - 1);
if(d2!=-1) {
splay(p->ch[d2],k2);
if(d==d2) rotate(o,d^1);
else rotate(o->ch[d],d);
}
rotate(o,d^1);
}
}
分裂与合并:
分裂:从序列从左第 k 个元素分裂,就是将序列的 o 的第 K 小元素伸展到根,断开树根与右子节点。
合并:将left部分最大的元素旋转到根,将right作为 left的右子树。(保证right>left所有元素)。
// 合并操作。假定left所有元素小于 right
Node* merge(Node* left,Node* right) {
splay(left,left->s);
left->ch[1] = right;
left->maintain();
return left;
}
//把 o 前 k 个小结点放到left里面,其他放到ritht里面,如果不够right = null
void split(Node* o,int k,Node* &left,Node* &right) {
splay(o,k);
left = o;
right = o->ch[1];
o->ch[1] = null;
left->maintain();
}
有时,对于序列有反转操作,这时,利用 线段树的 lazy标记,标记某一段是否反转。
对于,数据结构的定义:用一个Node数组,和一个Node 的 root指针,指向这个数组的元素。
#include <bits/stdc++.h>
using namespace std;
struct Node {
Node *ch[];
int s;
int flip;
int v;
int cmp(int k) const {
int d = k - ch[]->s;
if(d==) return -; //序列第 k 个找到
return d <= ? : ;
}
void maintain() {
s = ch[]->s + ch[]->s + ;
}
void pushdown() {
if(flip) {
flip = ;
swap(ch[],ch[]);
ch[]->flip = !ch[]->flip;
ch[]->flip = !ch[]->flip;
}
}
};
Node *null = new Node();
// d = 0 左旋
void rotate(Node* &o,int d) {
Node* k = o->ch[d^];
o->ch[d^] = k->ch[d];
k->ch[d] = o;
o->maintain();
k->maintain();
o = k;
}
//将序列左数第k个元素选择到根
void splay(Node* &o,int k) {
o->pushdown();
int d = o->cmp(k);
if(d==) k-=o->ch[]->s + ;
if(d!=-) {
Node* p = o->ch[d];
p->pushdown();
int d2 = p->cmp(k);
int k2 = (d2== ? k : k - p->ch[]->s - );
if(d2!=-) {
splay(p->ch[d2],k2);
if(d==d2) rotate(o,d^);
else rotate(o->ch[d],d);
}
rotate(o,d^);
}
}
// 合并操作。假定left所有元素小于 right
Node* merge(Node* left,Node* right) {
splay(left,left->s);
left->ch[] = right;
left->maintain();
return left;
}
//把 o 前 k 个小结点放到left里面,其他放到ritht里面,如果不够right = null
void split(Node* o,int k,Node* &left,Node* &right) {
splay(o,k);
left = o;
right = o->ch[];
o->ch[] = null;
left->maintain();
}
const int maxn = 1e5+;
struct SplaySequence {
int n;
Node seq[maxn];
Node *root;
Node* build(int sz) {
if(!sz) return null;
Node* L = build(sz/);
Node* o = &seq[++n];
o->v = n;
o->ch[] = L;
o->ch[] = build(sz-sz/-);
o->flip = o ->s = ;
o->maintain();
return o;
}
void init(int sz) {
n = ;
null->s = ;
root = build(sz);
for(int i = ; i < sz; i++)
printf("%d ",seq[i].v);
puts("");
}
};
vector<int> ans;
void print(Node* o) {
if(o!=null) {
o->pushdown();
print(o->ch[]);
ans.push_back(o->v);
print(o->ch[]);
}
}
void debug(Node* o) {
if(o!=null) {
o->pushdown();
debug(o->ch[]);
printf("%d \n",o->v -);
debug(o->ch[]);
}
}
SplaySequence ss;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
ss.init(n+);
debug(ss.root);
for(int i = ; i < m; i++) {
int a,b;
scanf("%d%d",&a,&b);
Node* left,*mid,*right,*o;
split(ss.root,a,left,o);
split(o,b-a+,mid,right);
mid->flip ^=;
ss.root = merge(merge(left,right),mid);
}
print(ss.root);
for(int i = ; i < (int)ans.size(); i++)
printf("%d\n",ans[i]-);
return ;
}
Uva 11922 Splay的更多相关文章
- UVA 11922 Splay tree
UVA 11922 题意: 有n个数1~n 操作a,b表示取出第a~b个数,翻转后添加到数列的尾部 输入n,m 输入m条指令a,b 输出最终的序列 代码: #include<iostream&g ...
- UVa 11922 & splay的合并与分裂
题意: 1个1—n的排列,实现一下操作:将a—b翻转并移动至序列的最后. SOL: splay维护区间的裸题——不过平衡树的题目貌似都是裸的吧...就是看操作的复杂程度罢... 如何取区间呢,我们在s ...
- uva 11922 Permutation Transforme/splay tree
原题链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18902 伸展树的区间翻转剪切... 如下: #include< ...
- UVA 11922 Permutation Transformer(Splay Tree)
题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18902 [思路] 伸展树+打标记. 用伸展树维护这个序列,使得能 ...
- UVA - 11922 Permutation Transformer (splay)
题目链接 题意:你的任务是根据m条指令改变排列{!,2,3,...,n}.每条指令(a,b)表示取出第a~b个元素,翻转后添加到排列的尾部.输出最终序列. 解法:splay对区间分裂合并翻转,模板题. ...
- UVA 11922 Permutation Transformer (Splay树)
题意: 给一个序列,是从1~n共n个的自然数,接下来又m个区间,对于每个区间[a,b],从第a个到第b个从序列中分离出来,翻转后接到尾部.输出最后的序列. 思路: 这次添加了Split和Merge两个 ...
- UVA 11922 Permutation Transformer —— splay伸展树
题意:根据m条指令改变排列1 2 3 4 … n ,每条指令(a, b)表示取出第a~b个元素,反转后添加到排列尾部 分析:用一个可分裂合并的序列来表示整个序列,截取一段可以用两次分裂一次合并实现,粘 ...
- UVA 11922 伸展树Splay 第一题
上次ZOJ月赛碰到一个题目要求对序列中的某个区间求gcd,并且还要随时对某位数字进行修改 插入 删除,当时马上联想到线段树,但是线段树不支持增删,明显还是不可以的,然后就敲了个链表想暴力一下,结果TL ...
- UVa 11922 - Permutation Transformer 伸展树
第一棵伸展树,各种调试模板……TVT 对于 1 n 这种查询我处理的不太好,之前序列前后没有添加冗余节点,一直Runtime Error. 后来加上冗余节点之后又出了别的状况,因为多了 0 和 n+1 ...
随机推荐
- cas aqs lock之间的关系
CAS 对应cpu的硬件指令, 是最原始的原子操作 cas主要是在AtomicInteger AtomicXXX类中使用, 用于实现线程安全的自增操作 ++. 对应一个unsafe对象, 根据os平台 ...
- gradle cache目录(.gradle)剖析
https://zhuanlan.zhihu.com/p/26473930 gradle下载后会对文件路径进行修饰,本文给出反向解析,把文件路径修改为原始路径的办法. 之所以研究这个,本来的目的是为了 ...
- 数据段描述符和代码段描述符(一)——《x86汇编语言:从实模式到保护模式》读书笔记10
一.段描述符的分类 在上一篇博文中已经说过,为了使用段,我们必须要创建段描述符.80X86中有各种各样的段描述符,下图展示了它们的分类. 看了上图,你也许会说:天啊,怎么这么多段描述符啊!我可怎么记住 ...
- 用 Python 构建 web 应用
用 Python 构建 web 应用 如果说仅仅要用 Python 构建 web 应用,可以将 socket 连接.HTTP 原始请求和响应格式等涉及网络基础的东西交给现成的库来实现,只需要专注于 w ...
- Linux VFS机制简析(二)
Linux VFS机制简析(二) 接上一篇Linux VFS机制简析(一),本篇继续介绍有关Address space和address operations.file和file operations. ...
- Oracle 查询当前系统时间十分钟之前的记录,时间比较SQL
select * from t_register r ));
- HDU 1003 最大连续和
http://www.acmerblog.com/hdu-1003-Max-Sum-1258.html 这里难点只有求起始位置,把握状态变化就行.一般这种子序列问题,都可以用dp简化 #include ...
- css之margin,padding的百分比
注意:上下内边距与左右内边距一致:即上下内边距的百分数会相对于父元素宽度设置,而不是相对于高度. PS:而且是基于父元素内容的宽度(width属性的大小),不是基于父元素整个框架的宽度
- JDBC实现动态查询
一 概述 1.什么是动态查询? 从多个查询条件中随机选择若干个组合成一个DQL语句进行查询,这一过程叫做动态查询. 2.动态查询的难点 可供选择的查询条件多,组合情况多,难以一一列举. 3.最终查询语 ...
- iframe中插入代码并执行
最近有需求通过iframe插入代码.有蛮多方法,如下: 1 var iframe = document.getElementById('previewUrl'); 2 iframe.src = 'ab ...