题目链接: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
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
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 }