Portal Gun:[BZOJ1500][NOI2005]维修数列

  有一段时间没写博客了,最近在刚数据结构......各种板子背得简直要起飞,题目也是一大堆做不完,这里就挑一道平衡树的题来写写好了

  关于这道题...该说什么好呢...网上好多人评论这道题又是难啊,又是要调很久什么的,还讲这是道平衡树的 boos 级别的题...看了题后,好像也没有说的这么难吧,思路比较简单,代码也不是特别长,splay也还算好码,虽然我这边现学 splay 现做确实花了不少时间,但总体来说这道题还是比较简单的.

  题目中的很多操作( INSERT,DELETE,MAKE-SAME,REVERSE )都有一个共性,是要操作从 pos 开始,往后一段长度的区间,这儿就有这样一个小套路:

  1.对于 INSERT ,我们把 pos 旋转到根, pos+1 旋转到根的右儿子,显然这时 pos+1 的左儿子为空,那么我们要插入就好办了,将要插入的序列建成一颗小树,直接将小树的根插入到 pos 的左儿子就完成了整个操作.

  2. 同样,对于其他几个操作,我们需要调整的是 pospos+tot 这一段,像 INSERT 那样,将 pos 旋转到根, pos+tot 旋转到根的右儿子,这时要操作的就是 pos+tot 的左儿子,这应该很好理解吧.以上是这些操作的共性.

  详细的,对于 DELETE ,我们从 pos+tot 的左儿子开始,向下递归删除,为了防止空间的浪费,可以开个栈来存放被删除节点以备后面的 INSERT 使用. MAKE-SMAEREVERSE 也都是递归处理,给每个节点分别记 tagrev ,tag 表示是否进行 MAKE-SAME 操作, rev 表示是否 REVERSE ,就像线段树里的 lazy ,如果你想问 REVERSE 怎么完成, swap 不就得了...

  至于 GET-SUMMAX-SUM ,线段树你会吧......子节点递归更新父亲就好了......

  以下是代码( 函数有点多,建议从 main() 开始阅读,顺着各个操作去阅读这些函数会比较有条理 ):

  先容我吐槽几句:这个 gi() 好丑......本来我的 gi() 只有两行的,但两行的 gi() 放上来会超边框非常不爽,只好扩成现在这个......对于这个 gi() ,想吐槽的发到评论全就好了......

  这个 gi() 是不是优美多了, hhh

 

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #define inf (1<<30)
  5. #define maxn (510010)
  6. #define ll long long
  7. #define il inline
  8. #define RG register
  9. using namespace std;
  10. il int gi(){
  11. RG int x=0,q=1; RG char ch=getchar();
  12. while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  13. if( ch=='-' ) q=-1,ch=getchar();
  14. while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
  15. return q*x;
  16. }
  17. struct node{
  18. bool rev,tag;
  19. int fa,son[2],w,size,sum,lmax,rmax,ans;
  20. }t[maxn];//lmax表示左端点在内的最大区间和,rmax相反,ans为整个区间的最大区间和
  21. int n,Q,cnt,root,sta[maxn],top,a[maxn];
  22. il int newnode(){
  23. int num;
  24. if(top) num=sta[top--]; else num=++cnt;
  25. t[num].son[0]=t[num].son[1]=t[num].fa=0;
  26. t[num].tag=t[num].rev=0; t[num].size=1;
  27. t[num].sum=t[num].w=t[num].lmax=t[num].rmax=-inf;
  28. return num;
  29. }
  30. il void up(int x){
  31. if(!x) return ;
  32. int ls=t[x].son[0],rs=t[x].son[1];
  33. t[x].size=t[ls].size+t[rs].size+1;
  34. t[x].sum=t[ls].sum+t[rs].sum+t[x].w;
  35. t[x].lmax=max(t[ls].lmax,t[ls].sum+t[x].w+max(0,t[rs].lmax) );
  36. t[x].rmax=max(t[rs].rmax,t[rs].sum+t[x].w+max(0,t[ls].rmax) );
  37. t[x].ans=max( max(t[ls].ans,t[rs].ans),max(0,t[ls].rmax)+t[x].w+max(0,t[rs].lmax) );
  38. }
  39. il void build_tree(RG int x,RG int l,RG int r){
  40. RG int mid=(l+r)>>1;
  41. t[x].w=a[mid];
  42. if(l==r){
  43. t[x].sum=t[x].lmax=t[x].rmax=t[x].ans=t[x].w;
  44. t[x].size=1; return ;
  45. }
  46. if(l<mid){
  47. t[x].son[0]=newnode(),t[ t[x].son[0] ].fa=x;
  48. build_tree(t[x].son[0],l,mid-1);
  49. }
  50. if(r>mid){
  51. t[x].son[1]=newnode(),t[ t[x].son[1] ].fa=x;
  52. build_tree(t[x].son[1],mid+1,r);
  53. }
  54. up(x);
  55. }
  56. il void reverse(int x){ //翻转的副操作
  57. if(!x) return ;
  58. swap(t[x].lmax,t[x].rmax); //!!别落下了!!
  59. swap(t[x].son[0],t[x].son[1]);
  60. t[x].rev^=1;
  61. }
  62. il void replace(int x,int v){ //修改的副操作
  63. if(!x) return ;
  64. t[x].w=v,t[x].sum=v*t[x].size;
  65. t[x].lmax=t[x].rmax=t[x].ans=max(v,v*t[x].size);
  66. t[x].tag=1,t[x].rev=0;
  67. }
  68. il void push_down(int x){ //标记下放
  69. if(t[x].rev){
  70. if( t[x].son[0] ) reverse(t[x].son[0]);
  71. if( t[x].son[1] ) reverse(t[x].son[1]);
  72. t[x].rev=0;
  73. }
  74. if(t[x].tag){
  75. if( t[x].son[0] ) replace(t[x].son[0],t[x].w);
  76. if( t[x].son[1] ) replace(t[x].son[1],t[x].w);
  77. t[x].tag=0;
  78. }
  79. }
  80. il void down(int x){ if(t[x].fa) down(t[x].fa); push_down(x); }
  81. il int get_kth(int x,int k){ //查第 k 个节点编号
  82. push_down(x);
  83. if(t[ t[x].son[0] ].size==k-1) return x;
  84. if(t[ t[x].son[0] ].size>k-1) return get_kth(t[x].son[0],k);
  85. else return get_kth(t[x].son[1],k-t[ t[x].son[0] ].size-1);
  86. }
  87. il int dir(int x){ return x==t[ t[x].fa ].son[1]; }
  88. il void rotate(int x){
  89. int y,z,a,b,c;
  90. y=t[x].fa,z=t[y].fa; b=dir(x),a=t[x].son[!b];
  91. if(z==0) root=x;
  92. else{ c=dir(y),t[z].son[c]=x; }
  93. t[x].fa=z,t[y].fa=x,t[x].son[!b]=y,t[y].son[b]=a;
  94. if(a) t[a].fa=y;
  95. up(y),up(x);
  96. }
  97. il void splay(int x,int goal){
  98. down(x);
  99. int y,z,b,c;
  100. while(t[x].fa!=goal){
  101. y=t[x].fa,z=t[y].fa;
  102. if(z==goal) rotate(x);
  103. else{
  104. b=dir(x),c=dir(y);
  105. if(b^c){ rotate(x),rotate(x); }
  106. else{ rotate(y),rotate(x); }
  107. }
  108. }
  109. }
  110. il void INSERT(int pos,int tot){ //插入
  111. for(RG int i=1;i<=tot;i++) a[i]=gi();
  112. int x=get_kth(root,pos+1); splay(x,0);
  113. int y=get_kth(t[x].son[1],1); splay(y,x);
  114. t[y].son[0]=newnode(); t[ t[y].son[0] ].fa=y;
  115. build_tree(t[y].son[0],1,tot);
  116. up(y),up(x);
  117. }
  118. il void erase(int x){ //删除的副操作
  119. if(!x) return ; sta[++top]=x;//回收节点
  120. if(t[x].son[0]) erase(t[x].son[0]);
  121. if(t[x].son[1]) erase(t[x].son[1]);
  122. }
  123. il void DELETE(int pos,int tot){ //删除
  124. int x=get_kth(root,pos); splay(x,0);
  125. int y=get_kth(t[x].son[1],tot+1); splay(y,x);
  126. erase( t[y].son[0] );
  127. t[ t[y].son[0] ].fa=0,t[y].son[0]=0;
  128. up(y),up(x);
  129. }
  130. il void MAKE_SAME(int pos,int tot,int v){ //修改
  131. int x=get_kth(root,pos); splay(x,0);
  132. int y=get_kth(t[x].son[1],tot+1); splay(y,x);
  133. replace(t[y].son[0],v);
  134. up(y),up(x);
  135. }
  136. il void REVERSE(int pos,int tot){ //翻转
  137. int x=get_kth(root,pos); splay(x,0);
  138. int y=get_kth(t[x].son[1],tot+1); splay(y,x);
  139. reverse(t[y].son[0]);
  140. up(y),up(x);
  141. }
  142. il int QUERY(int pos,int tot){ //查询
  143. int x=get_kth(root,pos); splay(x,0);
  144. int y=get_kth(t[x].son[1],tot+1); splay(y,x);
  145. return t[t[y].son[0]].sum;
  146. }
  147. il void init(){
  148. t[0].lmax=t[0].rmax=t[0].ans=-inf;
  149. cnt=2,root=1; //t[1],t[2]用来防止一开始建树时超出边界
  150. t[1].fa=0,t[1].size=2,t[1].son[1]=2,t[1].w=t[1].sum=t[1].lmax=t[1].rmax=-inf;
  151. t[2].fa=1,t[2].size=1,t[2].w=t[2].sum=t[2].lmax=t[2].rmax=-inf;
  152. n=gi(),Q=gi();
  153. for(RG int i=1;i<=n;i++) a[i]=gi();
  154. t[2].son[0]=newnode(),t[t[2].son[0]].fa=2;
  155. build_tree(t[2].son[0],1,n); up(2),up(1);
  156. }
  157. il void work(){
  158. int x,y,z; char s[15];
  159. while(Q--){
  160. scanf("%s",s);
  161. if(s[0]=='I'){ x=gi(),y=gi(); INSERT(x,y); }
  162. if(s[0]=='D'){ x=gi(),y=gi(); DELETE(x,y); }
  163. if(s[4]=='-'){ x=gi(),y=gi(),z=gi(); MAKE_SAME(x,y,z); }
  164. if(s[0]=='R'){ x=gi(),y=gi(); REVERSE(x,y); }
  165. if(s[0]=='G'){ x=gi(),y=gi(); printf("%d\n",QUERY(x,y)); }
  166. if(s[2]=='X') printf("%d\n",t[root].ans);
  167. }
  168. }
  169. int main(){ init(); work(); return 0; }//贯彻一行 main() 风格

  

  

[BZOJ1500][NOI2005]维修数列 解题报告 Splay的更多相关文章

  1. [BZOJ1500][NOI2005]维修数列---解题报告

    Portal Gun:[BZOJ1500][NOI2005]维修数列 有一段时间没写博客了,最近在刚数据结构......各种板子背得简直要起飞,题目也是一大堆做不完,这里就挑一道平衡树的题来写写好了 ...

  2. bzoj千题计划221:bzoj1500: [NOI2005]维修数列(fhq treap)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1500 1.覆盖标记用INF表示无覆盖标记,要求可能用0覆盖 2.代表空节点的0号节点和首尾的两个虚拟 ...

  3. splay模板三合一 luogu2042 [NOI2005]维护数列/bzoj1500 [NOI2005]维修数列 | poj3580 SuperMemo | luogu3391 【模板】文艺平衡树(Splay)

    先是维修数列 题解看这里,但是我写的跑得很慢 #include <iostream> #include <cstdio> using namespace std; int n, ...

  4. [bzoj1500][NOI2005]维修数列_非旋转Treap

    维修数列 bzoj-1500 NOI-2005 题目大意:给定n个数,m个操作,支持:在指定位置插入一段数:删除一个数:区间修改:区间翻转.查询:区间和:全局最大子序列. 注释:$1\le n_{ma ...

  5. BZOJ1500: [NOI2005]维修数列[splay ***]

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 12278  Solved: 3880[Submit][Statu ...

  6. [bzoj1500][NOI2005]维修数列——splay

    题目 题解 这道题可以说是数列问题的大BOSS,也算是这一周来学习splay等数据结构的一个总结. 我们一个一个地看这些操作. 对于操作1,我们首先建一棵子树,直接接上原树即可. 对于操作2,我们找到 ...

  7. BZOJ1500: [NOI2005]维修数列 [splay序列操作]【学习笔记】

    以前写过这道题了,但我把以前的内容删掉了,因为现在感觉没法看 重写! 题意: 维护一个数列,支持插入一段数,删除一段数,修改一段数,翻转一段数,查询区间和,区间最大子序列 splay序列操作裸题 需要 ...

  8. [bzoj1500][NOI2005 维修数列] (splay区间操作)

    Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目. 第2行包含N个数字,描述初始时的数列. 以下M行,每 ...

  9. BZOJ1500 [NOI2005]维修数列(Splay tree)

    [Submit][Status][Discuss] Description 请写一个程序,要求维护一个数列,支持以下 6 种操作: 请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格 Inp ...

随机推荐

  1. LeetCode 193. Valid Phone Numbers

    分析 难度 易 来源 https://leetcode.com/problems/valid-phone-numbers/ 题目 Given a text file file.txt that con ...

  2. webpack新手入门——配置及安装

    webpack 是一个现代 JavaScript 应用程序的静态模块打包器.当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的 ...

  3. Netty源码分析第7章(编码器和写数据)---->第1节: writeAndFlush的事件传播

    Netty源码分析第七章: 编码器和写数据 概述: 上一小章我们介绍了解码器, 这一章我们介绍编码器 其实编码器和解码器比较类似, 编码器也是一个handler, 并且属于outbounfHandle ...

  4. PHP--面向对象的设计原则

    三大特性是:封装.继承.多态 所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏. 封装是面向对象的特征之一,是对象和类概念的主要特 ...

  5. 使用sass与compass合并雪碧图(二)

    上一篇文章介绍了怎样使用compass合并雪碧图,生成的icons.css文件中单位是px,PC端可以直接在html文件中使用,但在移动端,我们需要根据不同分辨率的屏幕,来缩放图片大小,显然使用px单 ...

  6. vim相关命令单独记载

    1. 无敌的可扩展性 1.1 可扩展性给了软件强大的生命 曾几何时,Windows用户对软件的可扩展性没有概念,他们只能对他们使用的软件进行非常有限的定制.扩展软件的权利保留在软件开发者手中.软件的使 ...

  7. cnblogs.com用户体验

    一.是否提供了良好的体验给用户(同时提供价值)? 首先我觉得博客园给我们这些用户提供了良好的用户体验,博客园提供了一个纯净的技术交流空间,在这里我们可以找到几乎所有与IT技术有关的博文,而且可以在这里 ...

  8. web01-helloworld

    新建一个Java web项目,名字为web01 在项目中新建一个servlet,名字为SimpleHello 修改doGet()方法,为: public void doGet(HttpServletR ...

  9. KMP算法之next数组的求解思路

    2.next数组的求解思路 本部分内容转自:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algo ...

  10. Chapter 8 面向对象设计

    设计也是一个建模的活动,在设计阶段将集中研究系统的软件实现问题包括体系结构设计.详细设计.用户界面设计和数据库设计等.通常设计活动分为系统设计和详细设计两个主要阶段.软件设计要遵循模块化.耦合度和内聚 ...