3223: Tyvj 1729 文艺平衡树
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 5813 Solved: 3464
[Submit][Status][Discuss]
Description
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
Input
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
Output
输出一行n个数字,表示原始序列经过m次变换后的结果
Sample Input
1 3
1 3
1 4
Sample Output
HINT
N,M<=100000
Source
分析:splay上的区间操作.
一般的区间操作可以通过线段树解决,但是有一部分区间操作只能通过splay来解决.它在区间操作上和线段树有相通的地方:build,pushup,pushdown这些操作基本上都差不多,但是又有一些细微的差异.
以这题为例,先提取区间,将l-1转到根节点,再将r+1转到根节点的右节点.那么根节点的右节点的左子树就是要求的[l,r]区间.为了能够方便的提取区间[1,n],加入两个哨兵元素0,n + 1,那么实际操作的就是l,r+2.
翻转操作可以变成逐层翻转.就是将要翻转的区间在splay中的每一个点的左右子树交换,从上到下.为了提高效率,打个标记.那么实际上的翻转操作就都在pushdown中完成了.以后不管执行什么操作,若是从上到下,执行到x就要pushdown(x),最后pushup(x).
和线段树的一些小区别:建树是[l,mid - 1],[mid + 1,r],中间的mid不存在了.这样做我个人认为是因为splay中每个点代表一个元素,而线段树每个点代表一个区间. 在pushup的时候要加上当前节点的值!
最后中序遍历一遍就出来了,这里用到了一个原理:splay的中序遍历=原序列.
几个注意点:1.pushup注意顺序!先子树,后父亲. 2.输出判断当前点是否是哨兵元素. 3.因为插入了哨兵元素,所以每个元素的位置都要往后挪1. 4.splay的最后不要轻易换根,因为这不是直接旋转到根节点,而是旋转到某个点的下面,要先判断旋转到的点是不是0,是的话才能换根.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 200010; int n,m,tot,root,sizee[maxn]; struct node { int fa,left,right,v,tag; }e[maxn]; void update(int x) { sizee[x] = 1; if (e[x].left != 0) sizee[x] += sizee[e[x].left]; if (e[x].right != 0) sizee[x] += sizee[e[x].right]; } void pushdown(int x) { if (!x) return; if (e[x].tag) { e[e[x].left].tag ^= 1; e[e[x].right].tag ^= 1; e[x].tag = 0; int t = e[x].left; e[x].left = e[x].right; e[x].right = t; } } int build(int l,int r) //感觉这个建树不仅赋予了每个点代表的值,还标记了左右区间. { if (l > r) return 0; int x = ++tot; int mid = (l + r) >> 1; e[x].fa = e[x].left = e[x].right = e[x].tag = 0; e[x].v = mid; sizee[x] = 1; e[x].left = build(l,mid - 1); e[x].right = build(mid + 1,r); e[e[x].left].fa = e[e[x].right].fa = x; update(x); return x; } void turnr(int x) { pushdown(x); int y = e[x].fa; int z = e[y].fa; e[y].left = e[x].right; if (e[x].right != 0) e[e[x].right].fa = y; e[x].fa = z; if (z != 0) { if (e[z].left == y) e[z].left = x; else e[z].right = x; } e[x].right = y; e[y].fa = x; update(x); update(y); } void turnl(int x) { pushdown(x); int y = e[x].fa; int z = e[y].fa; e[y].right = e[x].left; if (e[x].left != 0) e[e[x].left].fa = y; e[x].fa = z; if (z != 0) { if (e[z].left == y) e[z].left = x; else e[z].right = x; } e[x].left = y; e[y].fa = x; update(x); update(y); } void splay(int x,int yy) { if (yy == 0) root = x; while (e[x].fa != yy) { pushdown(x); int y = e[x].fa; int z = e[y].fa; if (z == yy || z == 0) { if (x == e[y].left) turnr(x); else turnl(x); } else { if (e[z].left == y && e[y].left == x) { turnr(y); turnr(x); } else { if (e[z].right == y && e[y].right == x) { turnl(y); turnl(x); } else { if (e[z].left == y && e[y].right == x) { turnl(x); turnr(x); } else { turnr(x); turnl(x); } } } } } if (yy == 0) root = x; } int find(int x,int k) { pushdown(x); if (k > sizee[e[x].left] + 1) return find(e[x].right,k - 1 - sizee[e[x].left]); if (k == sizee[e[x].left] + 1) return x; return find(e[x].left,k); } void dfs(int x) { pushdown(x); if (e[x].left) dfs(e[x].left); if (e[x].v >= 1 && e[x].v <= n) printf("%d ",e[x].v); if (e[x].right) dfs(e[x].right); } int main() { scanf("%d%d",&n,&m); root = build(0,n + 1); for (int i = 1; i <= m; i++) { int l,r; scanf("%d%d",&l,&r); int p = find(root,l),q = find(root,r + 2); splay(p,0); splay(q,p); e[e[q].left].tag ^= 1; update(e[q].left); update(q); update(root); } dfs(root); return 0; }