[NOI2005]维修数列 Splay tree 区间反转,修改,求和,求最值

  题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1500

Description

Input

输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目。 第2行包含N个数字,描述初始时的数列。 以下M行,每行一条命令,格式参见问题描述中的表格。

Output

对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

Sample Input

9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM

Sample Output

-1
10
1
10

HINT

  反转和求和操作很好求,直接用两个标记rev和sum懒惰求值。对于区间最大值,每个区间需要维护3个信息:左端点开始的最大值,右端点的最大值,区间最大值。Push_Up

根据这三个值来就可以了,很好推。。。

  TlE+WA很多次,有很多要注意的地方。。。

  首先,Insert操作有很多,会使用过多的空间(大概100MB),如果不回收空间,会超时,我们可以人工压一个栈回收删除的节点。。。

  在初始化根节点的虚拟父亲节点0的时候,sum初始化为0,maxl、maxm和maxr需要初始化为-INF,因为如果一个节点没有两个儿子,那么会通过0节点来更新。

  Update_Same操作要注意如果节点不存在则不要更新,不然会影响0号节点。rev操作不仅仅要交换左右节点,还要交换左右最值。。。

  因为这里涉及反转和求最值操作,因此反转操作的延迟更新需要快于求最值的更新。。。

  基本这些问题都注意了,就差不多了。。。

  我开始在求MAX-SUM的时候是把初始增加的两个节点val初始化为0,然后用Push_Up维护更新信息,那么直接输出maxm[root]就可以了,但是WA了T^T。。。但是改为Splay()来维护,然后输出maxm[Key_value]就能A了(见代码上的注释),郁闷得死啊,求大神解释><..

  1 //STATUS:C++_AC_6060MS_23736KB
  2 #include <functional>
  3 #include <algorithm>
  4 #include <iostream>
  5 //#include <ext/rope>
  6 #include <fstream>
  7 #include <sstream>
  8 #include <iomanip>
  9 #include <numeric>
 10 #include <cstring>
 11 #include <cassert>
 12 #include <cstdio>
 13 #include <string>
 14 #include <vector>
 15 #include <bitset>
 16 #include <queue>
 17 #include <stack>
 18 #include <cmath>
 19 #include <ctime>
 20 #include <list>
 21 #include <set>
 22 #include <map>
 23 using namespace std;
 24 //using namespace __gnu_cxx;
 25 //define
 26 #define pii pair<int,int>
 27 #define mem(a,b) memset(a,b,sizeof(a))
 28 #define lson l,mid,rt<<1
 29 #define rson mid+1,r,rt<<1|1
 30 #define PI acos(-1.0)
 31 //typedef
 32 //typedef __int64 LL;
 33 //typedef unsigned __int64 ULL;
 34 //const
 35 const int N=500010;
 36 const int INF=0x3f3f3f3f;
 37 const int MOD=100000,STA=8000010;
 38 //const LL LNF=1LL<<60;
 39 const double EPS=1e-8;
 40 const double OO=1e15;
 41 const int dx[4]={-1,0,1,0};
 42 const int dy[4]={0,1,0,-1};
 43 const int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
 44 //Daily Use ...
 45 inline int sign(double x){return (x>EPS)-(x<-EPS);}
 46 template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;}
 47 template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
 48 template<class T> inline T lcm(T a,T b,T d){return a/d*b;}
 49 template<class T> inline T Min(T a,T b){return a<b?a:b;}
 50 template<class T> inline T Max(T a,T b){return a>b?a:b;}
 51 template<class T> inline T Min(T a,T b,T c){return min(min(a, b),c);}
 52 template<class T> inline T Max(T a,T b,T c){return max(max(a, b),c);}
 53 template<class T> inline T Min(T a,T b,T c,T d){return min(min(a, b),min(c,d));}
 54 template<class T> inline T Max(T a,T b,T c,T d){return max(max(a, b),max(c,d));}
 55 //End
 56 
 57 #define Key_value ch[ch[root][1]][0]
 58 int pre[N],ch[N][2];  //分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量
 59 int sz[N],st[N];   //子树规模,内存池
 60 int root,tot,top;   //根节点,根节点数量,内存池容量
 61 //题目特定数目
 62 int val[N],sum[N],maxl[N],maxm[N],maxr[N],num[N];
 63 bool rev[N],flag[N];
 64 int n,m,posi,all;
 65 //debug部分copy from hh
 66 void Treaval(int x) {
 67     if(x) {
 68         Treaval(ch[x][0]);
 69         printf("结点%2d:val = %2d 左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d maxm = %2d %2d %2d
",x,val[x],ch[x][0],ch[x][1],pre[x],sz[x],maxm[x],maxl[x],maxr[x]);
 70         Treaval(ch[x][1]);
 71     }
 72 }
 73 void debug() {printf("%d
",root);Treaval(root);}
 74 //以上Debug
 75 //新建一个结点
 76 void NewNode(int &x,int fa,int a)
 77 {
 78     if(top)x=st[top--];
 79     else x=++tot;
 80     sum[x]=val[x]=maxl[x]=maxm[x]=maxr[x]=a;
 81     pre[x]=fa;
 82     flag[x]=rev[x]=0;
 83     ch[x][0]=ch[x][1]=0;  //左右孩子为空
 84 }
 85 void Update_Same(int x,int v){
 86     if(!x) return;
 87     flag[x]=1;
 88     val[x]=v;
 89     sum[x]=sz[x]*v;
 90     maxl[x]=maxr[x]=maxm[x]=max(v,v*sz[x]);
 91 }
 92 void Update_Rev(int x){
 93     if(!x) return;
 94     swap(ch[x][0],ch[x][1]);
 95     swap(maxl[x],maxr[x]);  //翻转时左右区间也交换!!!!
 96     rev[x]^=1;
 97 }
 98 void Push_Up(int x)
 99 {
100     int ls=ch[x][0],rs=ch[x][1];
101     sz[x]=sz[ls]+sz[rs]+1;
102     sum[x]=sum[ls]+sum[rs]+val[x];
103     maxl[x]=Max(maxl[ls],sum[ls]+val[x]+Max(0,maxl[rs]));
104     maxr[x]=Max(maxr[rs],sum[rs]+val[x]+Max(0,maxr[ls]));
105     maxm[x]=Max(maxr[ls],0)+val[x]+Max(maxl[rs],0);
106     maxm[x]=Max(maxm[x],maxm[ls],maxm[rs]);
107 }
108 
109 void Push_Down(int x)
110 {
111     if(flag[x]){
112         Update_Same(ch[x][0],val[x]);
113         Update_Same(ch[x][1],val[x]);
114         flag[x]=0;
115     }
116     if(rev[x]){
117         Update_Rev(ch[x][0]);
118         Update_Rev(ch[x][1]);
119         rev[x]=0;
120     }
121 }
122 //旋转,kind为1为右旋,kind为0为左旋
123 void Rotate(int x,int kind)
124 {
125     int y=pre[x],z=pre[y];
126     Push_Down(y);
127     Push_Down(x);  //先把y的标记向下传递,再把x的标记往下传递
128     //类似SBT,要把其中一个分支先给父节点
129     ch[y][!kind]=ch[x][kind];
130     pre[ch[x][kind]]=y;
131     //如果父节点不是根结点,则要和父节点的父节点连接起来
132     if(z)ch[z][ch[z][1]==y]=x;
133     pre[x]=z;
134     ch[x][kind]=y;
135     pre[y]=x;
136     Push_Up(y);  //维护y结点,不要维护x节点,x节点会再次Push_Down,最后维护一下x节点即可
137 }
138 //Splay调整,将根为r的子树调整为goal
139 void Splay(int x,int goal)
140 {
141     int y,z,kind;
142     while(pre[x]!=goal){
143         //父节点即是目标位置,goal为0表示,父节点就是根结点
144         y=pre[x];
145         Push_Down(pre[y]);Push_Down(y);Push_Down(x);   //设计到反转操作,要先更新,然后在判断!!
146         if(pre[y]==goal){
147             Rotate(x,ch[y][0]==x);
148         }
149         else {
150             kind=ch[pre[y]][0]==y;
151             //两个方向不同,则先左旋再右旋
152             if(ch[y][kind]==x){
153                 Rotate(x,!kind);
154                 Rotate(x,kind);
155             }
156             //两个方向相同,相同方向连续两次
157             else {
158                 Rotate(y,kind);
159                 Rotate(x,kind);
160             }
161         }
162     }
163     //更新根结点
164     Push_Up(x);
165     if(goal==0)root=x;
166 }
167 
168 void RotateTo(int k,int goal)
169 {
170     int x=root;
171     Push_Down(x);
172     while(sz[ch[x][0]]!=k){
173         if(sz[ch[x][0]]>k)
174             x=ch[x][0];
175         else {
176             k-=sz[ch[x][0]]+1;
177             x=ch[x][1];
178         }
179         Push_Down(x);
180     }
181     Splay(x,goal);
182 }
183 //建树,中间结点先建立,然后分别对区间两端在左右子树建立
184 void BuildTree(int &x,int l,int r,int fa)
185 {
186     if(l>r)return;
187     int mid=(l+r)>>1;
188     NewNode(x,fa,num[mid]);
189     BuildTree(ch[x][0],l,mid-1,x);
190     BuildTree(ch[x][1],mid+1,r,x);
191     Push_Up(x);
192 }
193 
194 void Init()
195 {
196     root=top=tot=0;
197     ch[0][0]=ch[0][1]=sz[0]=pre[0]=0;
198     val[0]=rev[0]=sum[0]=flag[0]=0;
199     maxl[0]=maxm[0]=maxr[0]=-INF;
200 
201     for(int i=0;i<n;i++)
202         scanf("%d",num+i);
203     NewNode(root,0,0);
204     NewNode(ch[root][1],root,0);
205     BuildTree(Key_value,0,n-1,ch[root][1]);
206     Push_Up(ch[root][1]);
207     Push_Up(root);
208 }
209 
210 void Insert()
211 {
212     RotateTo(posi,0);
213     RotateTo(posi+1,root);
214     BuildTree(Key_value,0,all-1,ch[root][1]);
215     Push_Up(ch[root][1]);
216     Push_Up(root);
217 }
218 
219 void erase(int r){
220     if(!r) return;
221     st[++top]=r;
222     erase(ch[r][0]);
223     erase(ch[r][1]);
224 }
225 
226 void Delete()
227 {
228     RotateTo(posi-1,0);
229     RotateTo(posi+all,root);
230     erase(Key_value);
231     pre[Key_value]=0;
232     Key_value=0;
233     Push_Up(ch[root][1]);
234     Push_Up(root);
235 }
236 
237 void Update(int same)
238 {
239     RotateTo(posi-1,0);
240     RotateTo(posi+all,root);
241     int x=Key_value;
242     if(x==0)return;
243     flag[x]=1;
244     val[x]=same;
245     sum[x]=sz[x]*same;
246     maxl[x]=maxm[x]=maxr[x]=Max(same,same*sz[x]);
247     Push_Up(ch[root][1]);
248     Push_Up(root);
249 }
250 
251 void Reverse()
252 {
253     RotateTo(posi-1,0);
254     RotateTo(posi+all,root);
255     Update_Rev(Key_value);
256  /* Push_Up维护  Wa...
257     Push_Up(ch[root][1]);
258     Push_Up(root);  */
259 }
260 
261 int main()
262 {
263  //   freopen("in.txt","r",stdin);
264     int i,j,k,a;
265     char op[15];
266     while(~scanf("%d%d",&n,&m))
267     {
268         Init();
269         while(m--){
270             scanf("%s",op);
271             if(op[0]=='I'){
272                 scanf("%d%d",&posi,&all);
273                 for(i=0;i<all;i++)
274                     scanf("%d",num+i);
275                 Insert();
276             }
277             else if(op[0]=='D'){
278                 scanf("%d%d",&posi,&all);
279                 Delete();
280             }
281             else if(op[2]=='K'){
282                 scanf("%d%d%d",&posi,&all,&a);
283                 Update(a);
284             }
285             else if(op[0]=='R'){
286                 scanf("%d%d",&posi,&all);
287                 Reverse();
288             }
289             else if(op[0]=='G'){
290                 scanf("%d%d",&posi,&all);
291                 RotateTo(posi-1,0);
292                 RotateTo(posi+all,root);
293                 printf("%d
",sum[Key_value]);
294             }
295             else {
296                 RotateTo(0,0);
297                 RotateTo(sz[root]-1,root);
298                 printf("%d
",maxm[Key_value]);
299              /* 如果Push_Up维护,那么不需要上面的Totate()来维护  wa...
300                 printf("%d
",maxm[root]);   */
301             }
302         }
303     }
304     return 0;
305 }
原文地址:https://www.cnblogs.com/zhsl/p/3227535.html