hdu3487 (splay伸展树 区间翻转,切割,插入)
2015-05-31 09:49
330 查看
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <string.h> #include <queue> #define N 300010 using namespace std; //hdu3487 (splay伸展树 区间翻转,切割,插入) int pre , key , ch [2], size , rev , root, tot, n, cnt; //rev是翻转的延时标志 /* 伸展树的一点心得--- rotate、Splay操作是不影响伸展树原有的性质(只要是节点的左孩子,那它就小于节点,节点的右孩子那他就大于节点) 判断节点(k)在其父亲节点(x)的左孩子还是右孩子时用ch[x][ch[x][1]==k]=k 只要是建立起了一棵满足伸展树性质的,那都可以使用rotate、Splay操作,伸展树性质不变 另外对于要pushdown或update,都要记得时常更新,否则很容易TLE */ void update(int x) //区间维护 { size[x]=size[ch[x][0]]+size[ch[x][1]]+1; return ; } void pushdown(int x) //传递下去 { if(rev[x]) { int k=ch[x][0]; ch[x][0]=ch[x][1]; ch[x][1]=k; rev[ch[x][0]]^=1; rev[ch[x][1]]^=1; rev[x]=0; } return ; } void newnode(int &r, int father, int k) //初始化 { r=++tot; pre[r]=father; key[r]=k; ch[r][0]=ch[r][1]=rev[r]=0; size[r]=1; return ; } void build(int l, int r, int father, int &k) //分治法,建立伸展树 { if(l>r)return ; int mid=(l+r)>>1; newnode(k, father, mid); build(l, mid-1, k, ch[k][0]); build(mid+1, r, k, ch[k][1]); update(k); return ; } void init()//数据初始化,开头建立两个节点,一个是伸展树的根(相当于最小),一个是伸展树根的右节点(相当于最大)----作为边界对程序操作无影响,因为你截取的区间都在这中间 { root=tot=ch[0][0]=ch[0][1]=pre[0]=size[0]=rev[0]=0; newnode(root, 0, -1); newnode(ch[root][1], root, -1); update(root); build(1, n, ch[root][1], ch[ch[root][1]][0]); return ; } void rotate(int x, int kind) { int k=pre[x]; pushdown(k); pushdown(x); ch[k][!kind]=ch[x][kind]; pre[ch[x][kind]]=k; ch[x][kind]=k; pre[x]=pre[k]; if(pre[k]) ch[pre[k]][ch[pre[k]][1]==k]=x; pre[k]=x; update(k); return ; } void Splay(int x, int goal) { int k, g, h; pushdown(x); while(pre[x]!=goal) { k=pre[x]; if(pre[k]==goal) { rotate(x, ch[k][0]==x); } else { g=(ch[k][0]==x?0:1); h=(ch[pre[k]][0]==k?0:1); if(g==h) { rotate(k, !g); rotate(x, !g); } else { rotate(x, !g); rotate(x, g); } } } if(goal==0)root=x; update(x); return ; } int select(int k, int r) { pushdown(r); if(size[ch[r][0]]+1==k) return r; else if(size[ch[r][0]]+1>k) return select(k, ch[r][0]); else return select(k-size[ch[r][0]]-1, ch[r][1]); } void reversal(int l, int r) { int j, g; j=select(l, root); g=select(r+2, root); Splay(j, 0); Splay(g, root); rev[ch[g][0]]^=1; //异或 return ; } int getmin(int r) { int s=r; pushdown(r); //注意pushdown下去,否则TLE while(r) { s=r; r=ch[r][0]; pushdown(r); } return s; } void cut(int l, int r, int k) //切割区间并插入 { int j, g, h; j=select(l, root); //因为开头已经建立了一个最小的节点,每个输入的元素的排名要+1 g=select(r+2, root); Splay(j, 0); Splay(g, root); h=ch[g][0]; //其左子树就是实际[l,r]区间里的数 ch[g][0]=0; update(g); update(j); j=select(k+1, root); Splay(j, 0); g=getmin(ch[root][1]); Splay(g, root); ch[g][0]=h;//相当于插到k后面 pre[ch[g][0]]=g; update(g); update(j); return ; } void print(int r) //输出,递归从伸展树的左边到伸展树的右边 { if(cnt==n)return ; if(r==0)return ; pushdown(r); print(ch[r][0]); if(key[r]>0) { printf("%d", key[r]); cnt++; if(cnt<n)printf(" "); } print(ch[r][1]); } int main() { int m, a, b, c; char p[10]; while(scanf("%d%d", &n, &m)!=EOF) { if(n<0&&m<0)break; init(); while(m--) { scanf("%s", &p); if(p[0]=='C') { scanf("%d%d%d", &a, &b, &c); cut(a, b, c); } if(p[0]=='F') { scanf("%d%d", &a, &b); reversal(a, b); } } cnt=0; print(root); printf("\n"); } return 0; }
相关文章推荐
- Combination Sum
- Android TextView里直接显示图片的三种方法
- HTML 表单(form) 使用详解
- Redhat6.2升级为Redhat6.3 (linux内核升级)
- 回调函数理解-以Fragment为例
- Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载)
- NOI2004 郁闷的出纳员
- Java web基础总结十之—— jsp EL表达式
- OpenGL_ES加载TGA/BMP纹理
- hdu4217 Data Structure?
- 【Android】工程中文件的关联性
- [转]动态规划解最长公共子序列问题
- Linux下whereis和which的区别
- percona-toolkit-2.2.14-1安装时Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY
- 工厂模式
- 让一类只能产生一个实例对象
- 全屏slider--swiper
- 面试题之final,finally和finalize的区别以及如果catch里面有return语句,请问finally里面的代码还会执行吗?
- 鸟哥的linux私房菜学习笔记 ---第5章-2
- 玩转VIM-札记(三)