[BZOJ 1500] [NOI2005] 维修数列
题目链接:BZOJ - 1500
题目分析
我要先说一下,这道题我写了一晚上,然后Debug了一整个白天..........再一次被自己的蒟蒻程度震惊= =
这道题是传说中的Splay维护数列的Boss题目。
前面的几个操作和询问看起来比较正常,就是最后一个维护最大区间和比较复杂。
其实这个也并不是十分复杂,只是要多维护一点东西,事实证明,我代码里的错误都不是与这个询问有关的。
维护每个节点的 Lmx[x] ,即这个节点的子树代表的区间的从左端开始的最大权值和。Rmx[x],同理。Mx[x],这个区间的最大权值和。
然而我的最大权值和都是可以为空的,也就是说如果只能取负数我就什么都不取,就是0。
题目规定的最大权值区间是不能为空的,所以我多维护了一个 Max[x],询问的时候判断如果 Max[x] < 0 ,就输出 Max[x],否则输出我维护的 Mx[x]。
............................然后就是我Debug了一天的东西.........................
经过一天悲剧的探索与尝试,我发现我的错误是出现在了Reverse操作有关的东西。
PushDown写得有非常严重的错误,下面来梳理一下正确的写法,以后要固定一下写法,不能一个代码一个写法...
首先,翻转一个区间的时候,假如这个区间子树的根节点是 x,就Reverse(x)。
之后,PushDown(x) 的时候,判断 Rev[x] 是否为 1,如果是1,就Reverse(Son[x][0]); Reverse(Son[x][1]);
注意,交换左右儿子的操作在Reverse(x)里完成。Rev[x]为1表示的是x的两个儿子还应被 Reverse,但是x的两个儿子的顺序已经是对的。是x的孙子的顺序需要被交换。
代码:
void Reverse(int x) { Rev[x] ^= 1; swap(Son[x][0], Son[x][1]); } void PushDown(int x) { if (Rev[x] == 0) return; if (Son[x][0]) Reverse(Son[x][0]); if (Son[x][1]) Reverse(Son[x][1]); Rev[x] = 0; }
代码
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int MaxN = 1000000 + 5, INF = 999999999; inline void Read(int &Num) { char c = getchar(); bool Neg = false; while (c < '0' || c > '9') { if (c == '-') Neg = true; c = getchar(); } Num = c - '0'; c = getchar(); while (c >= '0' && c <= '9') { Num = Num * 10 + c - '0'; c = getchar(); } if (Neg) Num = -Num; } int n, m, Root, Index, Tot1, Tot2, Len; int V[MaxN], A[MaxN], Father[MaxN], Son[MaxN][2], Sum[MaxN], Rev[MaxN], Stack[MaxN]; int Max[MaxN], Size[MaxN], Lmx[MaxN], Rmx[MaxN], Mx[MaxN], D[MaxN]; bool Rep[MaxN]; char Str[15]; inline int gmax(int a, int b) {return a > b ? a : b;} inline int gmin(int a, int b) {return a < b ? a : b;} inline void Update(int x) { Size[x] = Size[Son[x][0]] + Size[Son[x][1]] + 1; Sum[x] = Sum[Son[x][0]] + Sum[Son[x][1]] + A[x]; Max[x] = gmax(A[x], gmax(Max[Son[x][0]], Max[Son[x][1]])); Lmx[x] = gmax(Lmx[Son[x][0]], Sum[Son[x][0]] + A[x] + Lmx[Son[x][1]]); Rmx[x] = gmax(Rmx[Son[x][1]], Sum[Son[x][1]] + A[x] + Rmx[Son[x][0]]); Mx[x] = gmax(Rmx[Son[x][0]] + A[x] + Lmx[Son[x][1]], gmax(Rmx[x], Lmx[x])); Mx[x] = gmax(Mx[x], gmax(Mx[Son[x][0]], Mx[Son[x][1]])); } inline void Replace(int x, int Num) { Rep[x] = true; D[x] = Num; A[x] = Num; Sum[x] = Num * Size[x]; Max[x] = Num; Lmx[x] = Rmx[x] = Mx[x] = gmax(0, Sum[x]); } inline void Reverse(int x) { Rev[x] ^= 1; swap(Son[x][0], Son[x][1]); swap(Lmx[x], Rmx[x]); } inline void PushDown(int x) { if (Rep[x]) { if (Son[x][0]) Replace(Son[x][0], D[x]); if (Son[x][1]) Replace(Son[x][1], D[x]); Rep[x] = false; } if (Rev[x]) { if (Son[x][0]) Reverse(Son[x][0]); if (Son[x][1]) Reverse(Son[x][1]); Rev[x] = 0; } } int NewNode(int a) { int x; if (Tot2 > 0) x = Stack[Tot2--]; else x = ++Tot1; Size[x] = 1; Son[x][0] = Son[x][1] = 0; Father[x] = 0; A[x] = Sum[x] = Max[x] = a; Lmx[x] = Rmx[x] = Mx[x] = gmax(a, 0); Rev[x] = 0; Rep[x] = false; return x; } int Build(int s, int t) { int x, m = (s + t) >> 1; x = NewNode(V[m]); if (s < m) { Son[x][0] = Build(s, m - 1); Father[Son[x][0]] = x; } if (t > m) { Son[x][1] = Build(m + 1, t); Father[Son[x][1]] = x; } Update(x); return x; } void Rotate(int x, int f) { int y = Father[x]; if (y == 0) return; Son[y][f ^ 1] = Son[x][f]; if (Son[x][f]) Father[Son[x][f]] = y; Father[x] = Father[y]; if (Father[y]) { if (y == Son[Father[y]][0]) Son[Father[y]][0] = x; else Son[Father[y]][1] = x; } Son[x][f] = y; Father[y] = x; Update(y); Update(x); } void Splay(int x, int d) { if (x == d) return; int y; while (Father[x] != d) { y = Father[x]; if (Father[y] == d) { if (x == Son[y][0]) Rotate(x, 1); else Rotate(x, 0); break; } if (y == Son[Father[y]][0]) { if (x == Son[y][0]) { Rotate(y, 1); Rotate(x, 1); } else { Rotate(x, 0); Rotate(x, 1); } } else { if (x == Son[y][1]) { Rotate(y, 0); Rotate(x, 0); } else { Rotate(x, 1); Rotate(x, 0); } } } if (Father[x] == 0) Root = x; } int Find(int Num) { int x = Root, k = Num; PushDown(x); while (Size[Son[x][0]] + 1 != k) { if (Size[Son[x][0]] + 1 > k) { x = Son[x][0]; } else { k -= Size[Son[x][0]] + 1; x = Son[x][1]; } PushDown(x); } return x; } void Delete(int x) { if (x == 0) return; if (Son[x][0]) Delete(Son[x][0]); if (Son[x][1]) Delete(Son[x][1]); Stack[++Tot2] = x; } int main() { scanf("%d%d", &n, &m); Tot1 = Tot2 = 0; Len = n; for (int i = 1; i <= n; ++i) Read(V[i]); Max[0] = -INF; int Temp; Root = Build(1, n); Splay(Find(1), 0); Temp = NewNode(-INF); Son[Root][0] = Temp; Father[Temp] = Root; Update(Root); Splay(Find(n + 1), 0); Temp = NewNode(-INF); Son[Root][1] = Temp; Father[Temp] = Root; Update(Root); int Pos, Tot, New, Num, x, y; for (int i = 1; i <= m; ++i) { scanf("%s", Str); if (strcmp(Str, "INSERT") == 0) { Read(Pos); Read(Tot); Len += Tot; for (int j = 1; j <= Tot; ++j) scanf("%d", &V[j]); New = Build(1, Tot); x = Find(Pos + 1); y = Find(Pos + 2); Splay(x, 0); Splay(y, x); Son[y][0] = New; Father[New] = y; Update(y); Update(x); } else if (strcmp(Str, "DELETE") == 0) { Read(Pos); Read(Tot); Len -= Tot; x = Find(Pos); y = Find(Pos + Tot + 1); Splay(x, 0); Splay(y, x); Delete(Son[y][0]); Son[y][0] = 0; Update(y); Update(x); } else if (strcmp(Str, "MAKE-SAME") == 0) { Read(Pos); Read(Tot); Read(Num); x = Find(Pos); y = Find(Pos + Tot + 1); Splay(x, 0); Splay(y, x); Replace(Son[y][0], Num); Update(y); Update(x); } else if (strcmp(Str, "REVERSE") == 0) { Read(Pos); Read(Tot); x = Find(Pos); y = Find(Pos + Tot + 1); Splay(x, 0); Splay(y, x); Reverse(Son[y][0]); Update(y); Update(x); } else if (strcmp(Str, "GET-SUM") == 0) { Read(Pos); Read(Tot); x = Find(Pos); y = Find(Pos + Tot + 1); Splay(x, 0); Splay(y, x); printf("%d\n", Sum[Son[y][0]]); } else if (strcmp(Str, "MAX-SUM") == 0) { x = Find(1); y = Find(Len + 2); Splay(x, 0); Splay(y, x); if (Max[Son[y][0]] <= 0) printf("%d\n", Max[Son[y][0]]); else printf("%d\n", Mx[Son[y][0]]); } } return 0; }