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的更多相关文章

  1. UVA 11922 Splay tree

    UVA 11922 题意: 有n个数1~n 操作a,b表示取出第a~b个数,翻转后添加到数列的尾部 输入n,m 输入m条指令a,b 输出最终的序列 代码: #include<iostream&g ...

  2. UVa 11922 & splay的合并与分裂

    题意: 1个1—n的排列,实现一下操作:将a—b翻转并移动至序列的最后. SOL: splay维护区间的裸题——不过平衡树的题目貌似都是裸的吧...就是看操作的复杂程度罢... 如何取区间呢,我们在s ...

  3. uva 11922 Permutation Transforme/splay tree

    原题链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18902 伸展树的区间翻转剪切... 如下: #include< ...

  4. UVA 11922 Permutation Transformer(Splay Tree)

    题目链接: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18902 [思路] 伸展树+打标记. 用伸展树维护这个序列,使得能 ...

  5. UVA - 11922 Permutation Transformer (splay)

    题目链接 题意:你的任务是根据m条指令改变排列{!,2,3,...,n}.每条指令(a,b)表示取出第a~b个元素,翻转后添加到排列的尾部.输出最终序列. 解法:splay对区间分裂合并翻转,模板题. ...

  6. UVA 11922 Permutation Transformer (Splay树)

    题意: 给一个序列,是从1~n共n个的自然数,接下来又m个区间,对于每个区间[a,b],从第a个到第b个从序列中分离出来,翻转后接到尾部.输出最后的序列. 思路: 这次添加了Split和Merge两个 ...

  7. UVA 11922 Permutation Transformer —— splay伸展树

    题意:根据m条指令改变排列1 2 3 4 … n ,每条指令(a, b)表示取出第a~b个元素,反转后添加到排列尾部 分析:用一个可分裂合并的序列来表示整个序列,截取一段可以用两次分裂一次合并实现,粘 ...

  8. UVA 11922 伸展树Splay 第一题

    上次ZOJ月赛碰到一个题目要求对序列中的某个区间求gcd,并且还要随时对某位数字进行修改 插入 删除,当时马上联想到线段树,但是线段树不支持增删,明显还是不可以的,然后就敲了个链表想暴力一下,结果TL ...

  9. UVa 11922 - Permutation Transformer 伸展树

    第一棵伸展树,各种调试模板……TVT 对于 1 n 这种查询我处理的不太好,之前序列前后没有添加冗余节点,一直Runtime Error. 后来加上冗余节点之后又出了别的状况,因为多了 0 和 n+1 ...

随机推荐

  1. 修改Android解锁界面

    解锁界面效果类似如下 其实这也是我后面做出的效果,样机因为没有ROOT不好调试截图,功能是将解锁的图标添加了最近运行的运用的图标,这样的话更方便用户去使用 1.分析 1.1.Android锁屏功能分析 ...

  2. Tomcat 启动很慢?

    Tomcat 8启动很慢,在启动中, 发现加载如下类时很慢: org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom 原因 ...

  3. 如何看linux是32位还是64位--转

    地址:http://hi.baidu.com/hehongrong/item/20c296bcf8d834432aebe3b2 如何看linux是32位还是64位 如何看linux是32位还是64位 ...

  4. python 模板注入

    今天学习了python的模板注入,这里自己搭建环境测试以下,参考文章:http://www.freebuf.com/articles/web/136118.html web 程序包括两个文件: fla ...

  5. 从函数作用域和块级作用域看javascript的作用域链

    在ES6之前,javascript只有全局作用域和函数作用域.所谓作用域就是一个变量定义并能够被访问到的范围.也就是说如果一个变量定义在全局(window)上,那么在任何地方都能访问到这个变量,如果这 ...

  6. json数据的存储与读取

    1.  json数据格式: data = [ {"key1":"xxx","item":"ddd"}, {"k ...

  7. 数据库中char、varchar、varchar2、nvarchar之间的关系

    符串“abc",对于CHAR (20),表示你存储的字符将占20个字节(包括17个空字符),而同样的VARCHAR2 (20)则只占用3个字节的长度,20只是最大值,当你存储的字符小于20时 ...

  8. js图片上传并预览

    <form id="addpic" class="easyui-form" method="post" enctype="m ...

  9. html页面的加载顺序

    页面加载顺序: 解析HTML结构加载外部脚本和样式表文件解析并执行脚本代码构造HTML DOM模型加载图片等外部文件页面加载完毕 window.onload = function () {  }  / ...

  10. 支持触屏的zepto轮播图插件

    占个座,有时间再写,呵呵 关于zepto.js,官方标准版是不支持touch的.可以去github下载压缩包,里面有所有支持的模块.我用的zepto.js,是经过打包的,包括polyfill zept ...