Description
请写一个程序,要求维护一个数列,支持以下 6 种操作:(请注意,格式栏 中的下划线‘ _ ’表示实际输入文件中的空格)
(N) 表示初始时数列中数的个数,(M) 表示要进行的操作数目。
Hint
任何时刻数列中最多含有 (5 imes 10^5) 个数。
任何时刻数列中任何一个数字均在 ([-10^3,10^3]) 内。
(1le Mle 2 imes 10^4) ,插入的数字总数不超过 (4 imes 10^6) 。
Solution
这个可以说是 Splay 维护数列的板题了。用 Splay 作为区间树,关键字可以理解为 位置。
首先建树,加入虚拟结点,放置某些时候操作到边上是出现一些奇奇怪怪的错误。
先扯一下 Splay 做简单区间操作的一些套路—— 子树提取。
假设要提取区间 ([l, r]),那么只要找到第 (l) 个和第 (r + 2) 个结点(原来是第 (l-1, r+1) 个,但是要注意最开头有一个虚拟结点),令其分别为结点 (x, y);
接下来直接 splay(x, 0), splay(r, l)
,就行了,结点 (y) 的左子树即为所求区间对应的子树的根:
然后,一个个操作来。
INSERT
操作
找到要插入的位置左边、右边的两个结点(注意有虚拟结点,需要 +1)。
然后两个结点上旋。
将插入数列建好树,之后在根的右儿子的左儿子的位置处插入建好的树。
最后 maintain 一下。
DELETE
操作
找到待删除数列的端点的 左/右一个位置 的结点,上旋。
使用一个垃圾回收机制,存下删掉的结点,等到重新建树时用。
ch[r][0] ← 0
,maintain。
MAKE-SAME
操作
找到待修改区间的端点的 左/右一个位置 的结点,上旋。
打一个标记,maintain。
REVERSE
操作
找到待修改区间的端点的 左/右一个位置 的结点,上旋。
打一个标记,maintain。
GET-SUM
操作
维护一个子树值和。
找到目标区间的端点的 左/右一个位置 的结点,上旋。
获取 sum 返回。
MAX-SUM
操作
有点麻烦,套路就是 GSS 的差不多。
维护一个最大前缀和,最大后缀和,最大子段和。
具体见代码。
时间复杂度 (O(nlog n)),空间复杂度 (O(n))。
坑点
最大子段和的子段必须至少选一个元素。
题目给的参数意义有点区别。
先将 0 号结点初始化一下。
Code
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : Luogu P2042 NOI2005 维护数列
*/
#include <iostream>
#include <algorithm>
#include <string>
#include <stack>
using namespace std;
const int N = 5e5 + 5;
const long long inf = 1e18;
typedef long long LL;
int ch[N][2], fa[N], size[N];
LL val[N], sum[N], lmax[N], rmax[N], mmax[N];
bool cov[N], rev[N];
LL ary[N];
int n, q, total = 0, root;
stack<int> rec;
inline int create() {
if (rec.empty()) return ++total;
int ret = rec.top();
return rec.pop(), ret;
}
inline void clear(int rt) {
ch[rt][0] = ch[rt][1] = fa[rt] = 0;
val[rt] = lmax[rt] = rmax[rt] = 0;
mmax[rt] = -inf;
cov[rt] = rev[rt] = 0;
}
inline void maintain(int rt) {
size[rt] = size[ch[rt][0]] + size[ch[rt][1]] + 1;
sum[rt] = sum[ch[rt][0]] + sum[ch[rt][1]] + val[rt];
lmax[rt] = max(lmax[ch[rt][0]], lmax[ch[rt][1]] + sum[ch[rt][0]] + val[rt]);
rmax[rt] = max(rmax[ch[rt][1]], rmax[ch[rt][0]] + sum[ch[rt][1]] + val[rt]);
mmax[rt] = max(max(mmax[ch[rt][0]], mmax[ch[rt][1]]), lmax[ch[rt][1]] + rmax[ch[rt][0]] + val[rt]);
}
inline void pushdown(int rt) {
int &l = ch[rt][0], &r = ch[rt][1];
if (cov[rt]) {
rev[rt] = cov[rt] = 0;
if (l) cov[l] = 1, val[l] = val[rt], sum[l] = val[rt] * size[l];
if (r) cov[r] = 1, val[r] = val[rt], sum[r] = val[rt] * size[r];
if (val[rt] >= 0) {
if (l) lmax[l] = rmax[l] = mmax[l] = sum[l];
if (r) lmax[r] = rmax[r] = mmax[r] = sum[r];
} else {
if (l) lmax[l] = rmax[l] = 0, mmax[l] = val[l];
if (r) lmax[r] = rmax[r] = 0, mmax[r] = val[r];
}
}
if (rev[rt]) {
rev[rt] = 0;
if (l) rev[l] ^= 1;
if (r) rev[r] ^= 1;
swap(lmax[l], rmax[l]);
swap(lmax[r], rmax[r]);
swap(ch[l][0], ch[l][1]);
swap(ch[r][0], ch[r][1]);
}
}
inline int get(int rt) {
return rt == ch[fa[rt]][1];
}
inline void rotate(int x) {
int y = fa[x], z = fa[y], c = get(x);
ch[y][c] = ch[x][c ^ 1];
fa[ch[x][c ^ 1]] = y;
ch[x][c ^ 1] = y;
fa[y] = x, fa[x] = z;
if (z) ch[z][y == ch[z][1]] = x;
maintain(y), maintain(x);
}
inline void splay(int x,int g) {
for (register int f = fa[x]; (f = fa[x]) != g; rotate(x))
if(fa[f] != g) rotate(get(f) == get(x) ? f : x);
if (!g) root = x;
}
inline int select(int k) {
for (register int rt = root; rt;) {
pushdown(rt);
if (k == size[ch[rt][0]] + 1) return rt;
if (k < size[ch[rt][0]] + 1) rt = ch[rt][0];
else k -= size[ch[rt][0]] + 1, rt = ch[rt][1];
}
throw;
}
int build(int l, int r, int f) {
if (l>r) return 0;
int rt = create(), mid = (l + r) >> 1;
if (l == r) {
val[rt] = sum[rt] = ary[mid];
size[rt] = 1;
ch[rt][0] = ch[rt][1] = 0;
fa[rt] = f;
lmax[rt] = rmax[rt] = max(0ll, val[rt]);
mmax[rt] = val[rt];
return rt;
}
val[rt] = ary[mid], fa[rt] = f;
ch[rt][0] = build(l, mid - 1, rt);
ch[rt][1] = build(mid + 1, r, rt);
return maintain(rt), rt;
}
inline void insert(int pos, int tot) {
if (!tot) return;
int l = select(pos + 1), r = select(pos + 2);
splay(l, 0), splay(r, l);
ch[r][0] = build(1, tot, r);
maintain(r), maintain(l);
}
void recycle(int rt) {
if (!rt) return;
rec.push(rt);
recycle(ch[rt][0]);
recycle(ch[rt][1]);
clear(rt);
}
inline void erase(int pos, int tot) {
if (!tot) return;
int l = select(pos), r = select(pos + tot + 1);
splay(l, 0), splay(r, l);
recycle(ch[r][0]), ch[r][0] = 0;
maintain(r), maintain(l);
}
inline void assign(int pos, int tot, LL c) {
if (!tot) return;
int l = select(pos), r = select(pos + tot + 1);
splay(l, 0), splay(r, l);
int x = ch[r][0];
val[x] = c, sum[x] = size[x] * c;
if (c >= 0) lmax[x] = rmax[x] = mmax[x] = sum[x];
else lmax[x] = rmax[x] = 0, mmax[x] = val[x];
cov[x] = 1;
maintain(r), maintain(l);
}
inline void reverse(int pos, int tot) {
if (!tot) return;
int l = select(pos), r = select(pos + tot + 1);
splay(l, 0), splay(r, l);
int x = ch[r][0];
if (cov[x]) return;
swap(ch[x][0], ch[x][1]);
swap(lmax[x], rmax[x]);
rev[x] ^= 1;
maintain(r), maintain(l);
}
inline LL getSum(int pos, int tot) {
if (!tot) return 0ll;
int l = select(pos), r = select(pos + tot + 1);
splay(l, 0), splay(r, l);
return sum[ch[r][0]];
}
inline LL getMaxSum() {
int l = select(1), r = select(size[root]);
splay(l, 0), splay(r, l);
return mmax[ch[r][0]];
}
signed main() {
ios::sync_with_stdio(0);
cin >> n >> q;
for (register int i = 1; i <= n; i++)
cin >> ary[i + 1];
ary[1] = ary[n + 2] = -inf;
clear(0);
root = build(1, n + 2, 0);
while (q--) {
string cmd;
int pos, tot;
LL c;
cin >> cmd;
if (cmd == "INSERT") {
cin >> pos >> tot;
for (register int i = 1; i <= tot; i++)
cin >> ary[i];
insert(pos, tot);
}
if (cmd == "DELETE") {
cin >> pos >> tot;
erase(pos, tot);
}
if (cmd == "MAKE-SAME") {
cin >> pos >> tot >> c;
assign(pos, tot, c);
}
if (cmd == "REVERSE") {
cin >> pos >> tot;
reverse(pos, tot);
}
if (cmd == "GET-SUM") {
cin >> pos >> tot;
cout << getSum(pos, tot) << endl;
}
if (cmd == "MAX-SUM")
cout << getMaxSum() << endl;
}
}