[BZOJ 1058] [ZJOI2007] 报表统计 【平衡树】
题目链接:BZOJ - 1058
题目分析
这道题看似是需要在序列中插入一些数字,但其实询问的内容只与相邻的元素有关。
那么我们只要对每个位置维护两个数 Ai, Bi, Ai 就是初始序列中 i 这个位置的数, Bi 是在 i 这个位置insert的最后一个数。
那么在 i insert一个数 Num 的时候,造成的影响就是使得 Bi 和 A(i+1) 不再相邻,同时使 Bi 与 Num, Num 与 A(i+1) 相邻。然后将 Bi 更新为 Num。
这样就只需要实现一个multiset的功能就可以了。可以手写平衡树,也可以使用STL,当然STL会慢许多。
代码
#include <iostream> #include <cstdlib> #include <cstring> #include <cmath> #include <cstdio> #include <algorithm> #include <map> #include <set> using namespace std; const int MaxN = 500000 + 5, INF = 1000000015, MaxL = 25; int n, m, MSG; int A[MaxN], B[MaxN]; char Str[MaxL]; multiset<int> S, S2; multiset<int>::iterator It, It2; 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 int Abs(int a) {return a < 0 ? -a : a;} void Before(int x) { It = S.lower_bound(x); if (It == S.end()) { It--; MSG = gmin(MSG, x - *It); return; } if (*It == x) { MSG = 0; return; } if (It == S.begin()) return; It--; MSG = gmin(MSG, x - *It); } void After(int x) { It = S.upper_bound(x); if (It == S.end()) return; MSG = gmin(MSG, *It - x); } int main() { scanf("%d%d", &n, &m); S.clear(); S2.clear(); for (int i = 1; i <= n; ++i) { scanf("%d", &A[i]); B[i] = A[i]; S.insert(A[i]); } for (int i = 2; i <= n; ++i) S2.insert(Abs(A[i] - A[i - 1])); MSG = INF; for (int i = 1; i <= n; ++i) { S.erase(S.find(A[i])); Before(A[i]); After(A[i]); S.insert(A[i]); } int Pos, Num; for (int i = 1; i <= m; ++i) { scanf("%s", Str + 1); if (Str[1] == 'I') { scanf("%d%d", &Pos, &Num); Before(Num); After(Num); S.insert(Num); if (Pos == n) { B[Pos] = Num; continue; } S2.erase(S2.find(Abs(A[Pos + 1] - B[Pos]))); S2.insert(Abs(Num - B[Pos])); S2.insert(Abs(A[Pos + 1] - Num)); B[Pos] = Num; } else if (Str[5] == 'G') { It2 = S2.begin(); printf("%d\n", *It2); } else { printf("%d\n", MSG); } } return 0; }