UVA 11922 Permutation Transformer —— splay伸展树

题意:根据m条指令改变排列1 2 3 4 … n ,每条指令(a, b)表示取出第a~b个元素,反转后添加到排列尾部

分析:用一个可分裂合并的序列来表示整个序列,截取一段可以用两次分裂一次合并实现,粘贴到末尾可以用一次合并实现。

翻转可以采用在每个结点上做标记的方法,flip = 1意味着将这棵子树翻转,可以类似线段树用一个pushdown()实现标记向下传递。

可以发现当前排列就是伸展树的中序遍历序列。中序遍历打印结果即可。

注意代码中设置了虚拟首结点0的技巧。

代码如下:

  1 #include <cstdio>
  2 #include <cstdlib>
  3 #include <iostream>
  4 #include <vector>
  5 using namespace std;
  6
  7 struct Node {
  8     Node* ch[2];
  9     int r, v, s;
 10     int flip;
 11     Node(int vv): v(vv) {
 12         r = rand();
 13         s = 1;
 14         ch[0] = ch[1] = NULL;
 15         flip = 0;
 16     }
 17     bool cmp(const int &x) const {
 18         if(x == v) return -1;
 19         return x < v ? 0 : 1;
 20     }
 21     void maintain() {
 22         s = 1;
 23         if(ch[0] != NULL) s += ch[0]->s;
 24         if(ch[1] != NULL) s += ch[1]->s;
 25     }
 26     void pushdown() {
 27         if(flip) {
 28             flip = 0;
 29             swap(ch[0], ch[1]);
 30             if(ch[0] != NULL) ch[0]->flip = !ch[0]->flip;
 31             if(ch[1] != NULL) ch[1]->flip = !ch[1]->flip;
 32         }
 33     }
 34 };
 35 void rotate(Node* &o, int d) {
 36     Node* k = o->ch[d^1]; o->ch[d^1] = k->ch[d]; k->ch[d] = o;
 37     o->maintain(); k->maintain(); o = k;
 38 }
 39 void insert(Node* &o, int x) {
 40     if(o == NULL) o = new Node(x);
 41     else {
 42         int d = o->cmp(x);
 43         insert(o->ch[d], x);
 44         if(o->ch[d]->r > o->r) rotate(o, d^1);
 45     }
 46     o->maintain();
 47 }
 48
 49 void splay(Node* &o, int k) {
 50     o->pushdown();
 51     int s = o->ch[0] == NULL ? 0 : o->ch[0]->s;
 52     int d = k <= s ? 0 : (k == s+1 ? -1 : 1);
 53     if(d == 1) k -= s+1;
 54     if(d != -1) {
 55         splay(o->ch[d], k);
 56         rotate(o, d^1);
 57     }
 58 }
 59
 60 Node* merge(Node* left, Node* right) {
 61     splay(left, left->s);
 62     left->ch[1] = right;
 63     left->maintain();
 64     return left;
 65 }
 66
 67 void split(Node* o, int k , Node* &left, Node* &right) {
 68     splay(o, k);
 69     left = o;
 70     right = o->ch[1];
 71     o->ch[1] = NULL;
 72     left->maintain();
 73 }
 74 vector<int> seq;
 75 void dfs(Node* o) {
 76     if(o == NULL) return;
 77     o->pushdown();
 78     dfs(o->ch[0]);
 79     if(o->v) {
 80         //printf("%d ", o->v);
 81         seq.push_back(o->v);
 82     }
 83     dfs(o->ch[1]);
 84 }
 85 int n, m;
 86 int main() {
 87     cin >> n >> m;
 88     Node* root = new Node(0); //虚拟首结点0,方便分裂操作
 89     for(int i = 1; i <= n; i++) insert(root, i);
 90     while(m--) {
 91         int a, b;
 92         cin >> a >> b;
 93         Node *left, *mid, *right, *o;
 94         split(root, a, left, o);
 95         split(o, b-a+1, mid, right);
 96         mid->flip ^= 1;
 97         root = merge(merge(left, right), mid);
 98     }
 99     dfs(root);
100     for(int i = 0; i < seq.size(); i++) cout<<seq[i]<<endl;
101     return 0;
102 }
时间: 2025-01-01 08:16:39

UVA 11922 Permutation Transformer —— splay伸展树的相关文章

uva 11922 - Permutation Transformer(伸展树)

题目链接:uva 11922 - Permutation Transformer 题目大意:给定一个序列,每次操作取出区间a~b,翻转后放到末尾,随后输出序列. 解题思路:就是伸展树,对于每个节点设一个flip,表示是否为翻转转态.每次将a旋转到根,然后分裂,再将b翻转到根,分裂,然后将mid翻转放到最后. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; str

11922 - Permutation Transformer (Splay区间翻转)

UVA 11922 - Permutation Transformer 题目链接 题意:给一个序列,每次操作选择(a,b),把(a,b)序列翻转之后加到末尾,求最终序列 思路:Splay的应用,多一个flip标记,在开头多一个虚拟的0结点,这样每次就利用Splay进行分裂合并即可 代码: #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include

uva 11922 Permutation Transforme/splay tree

原题链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18902 伸展树的区间翻转剪切... 如下: 1 #include<cstdio> 2 #include<cstdlib> 3 #include<iostream> 4 #include<algorithm> 5 const int Max_N = 100010; 6 struct Node{ 7 int v, s, rev;

Uva 11922 Permutation Transformer

闲的没事打了打一个splay,,,,, 注意只要是遍历数据结构就要下传标记!!!! 只要是修改就要维护所有受到影响的!!! 希望以后不要再犯这种zz错误了hhh #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<cstdlib> #define ll long long #define m

Splay伸展树学习笔记

Splay伸展树 有篇Splay入门必看文章 —— CSDN链接 经典引文 空间效率:O(n) 时间效率:O(log n)插入.查找.删除 创造者:Daniel Sleator 和 Robert Tarjan 优点:每次查询会调整树的结构,使被查询频率高的条目更靠近树根. Tree Rotation 树的旋转是splay的基础,对于二叉查找树来说,树的旋转不破坏查找树的结构. Splaying Splaying是Splay Tree中的基本操作,为了让被查询的条目更接近树根,Splay Tree

bzoj1208 splay伸展树

splay伸展树主要有两种操作形式 (1)正常的二叉树插入形式 功能:a.查找 b.求最大值 c.最小值 d.求前驱 e.求后继 f.删点 g.合并splay树 (这里的删除直接利用splay树的结点下标) (2)区间形式 (插入是以区间形式插入的) 区间形式的伸展树相当于线段树,支持线段树的所有操作,并且还支持区间插入这个功能, 比如操作区间[a,b],将根设为a-1,根的右孩子设为b+1,那么根的右孩子的左孩子就是所求区间 某个点插入区间也是一个道理 需要注意的是,这里init()自动生成了

【学时总结】◆学时&#183;VI◆ SPLAY伸展树

◆学时·VI◆ SPLAY伸展树 平衡树之多,学之不尽也-- ◇算法概述 二叉排序树的一种,自动平衡,由 Tarjan 提出并实现.得名于特有的 Splay 操作. Splay操作:将节点u通过单旋.双旋移动到某一个指定位置. 主要目的是将访问频率高的节点在不改变原顺序的前提下移动到尽量靠近根节点的位置,以此来解决同一个(相似)问题的多次查询. 但是在非降序查询每一个节点后,Splay 树会变为一条链,降低运算效率. ◇原理&细解 (1)旋转操作 二叉排序树必须满足 左儿子<根节点<右

Splay伸展树

伸展树,感觉对我这种菜鸟还是有些吃力,主要也是旋转的时候吧,把要查询的节点旋转到根节点,看网上是有两种方法,一是从上到下,一是从下到上.从上到下,是把树拆分成中树,左树和右树,始终保证目标节点在中树,不断向下把中树的节点添到右树或者左树,直到目标节点为中树的根,再将三树合并起来.另外一种从下到上,旋转操作类似AVL树中的旋转,但是判断好像不是很好写,我写的是从上到下的,另外删除也很巧妙,先把目标节点旋转到根,若此时左子树为空直接删除,否则找到左子树最右的节点当头,利用伸展树的特殊旋转就可以一步删

Splay伸展树入门(单点操作,区间维护)

ps:终于学会了伸展树的区间操作,做一个完整的总结,总结一下自己的伸展树的单点操作和区间维护,顺便给未来的总结复习用. splay是一种平衡树,[平均]操作复杂度O(nlogn).首先平衡树先是一颗二叉搜索树,刚刚开始学的时候找题hash数字的题先测板子... 后来那题被学长改了数据不能用平衡树测了...一道二分数字的题. 二叉搜索树的功能是,插入一个数字,在O(logn)的时间内找到它,并操作,插入删除等.但是可能会让二叉搜索树退化成链,复杂度达到O(n) 原文地址:https://www.c