维护一个数列, 支持6种操作: 区间插入, 区间删除, 区间赋值, 区间翻转, 区间求和, 求和最大的连续子列. (插入数字的总数不超过4*10^6, 任何时刻数列中最多含5*10^5个数, 任何一个数字均在[-1000, 1000]内, 操作数不超过20000) 这是一道经典的Splay模板题.
树上维护信息的方法 |
线段树的结点的信息由子区间合并而来, 平衡树的结点上不仅有子树信息, 还有它自己的信息. |
信息的维护分为两类, 自底向上和自顶向下. 举例. 自底向上: size, sum. 自顶向下: 各种标记, 如add, cover, reverse. |
自底向上的更新通常发生在修改后的回溯过程中, 旋转之后 (带旋转的平衡树). |
自顶向下的更新通常发生在访问之前. 需要保证访问之前, 结点的信息是最新的. 平衡树可以安全地进行旋转. 线段树上有时标记可以不下传, 只需计算时考虑它们的影响. 平衡树通常不行, 因为涉及形态的改变. |
标记需要满足三个性质: 1. 可以快速计算出对本结点的影响. 2. 可以相互合并. 3. 如果有多种标记, 它们能相容. |
本题需要维护v, size, sum, max_sum, max_pre, max_suf. 需要打赋值标记和翻转标记. 第一次打翻转标记的时候很惊奇, 没想到翻转区间还可以用这种递归的方式.
赋值标记: 影响到v, sum, max_sum, max_pre, max_suf.
翻转标记: 影响到max_pre, max_suf, 还得交换左右子树.
它们之间没有什么特殊的影响. 需要注意, 有时打翻转标记, 在下传的时候才swap, 这里不行, 因为涉及到max_pre和max_suf要为祖先所用.
另外, 本题内存限制较紧, 需要delete, 或者手写带释放的内存池.
使用前置声明就能在前面定义nil了. 一种避免忘记init的方法: 写在struct的构造函数里, 定义一个全局变量......
#include <bits/stdc++.h>
#define Rep(i, a, b) for (int i = (a), i##_ = (b); i < i##_; ++i)
#define For(i, a, b) for (int i = (a), i##_ = (b); i <= i##_; ++i)
#define Down(i, a, b) for (int i = (a), i##_ = (b); i >= i##_; --i)
using namespace std;
const int null = -1001, inf = 1e8 + 1;
struct Node;
Node* nil;
struct Node {
Node* ch[2], * fa;
int v, sz, sum, ms, pre, suf, c;
bool rev;
Node(): fa(0), v(null), sz(0), sum(0), ms(-inf), pre(0), suf(0), c(null), rev(false)
{
ch[0] = ch[1] = nil;
}
Node(int v): fa(0), v(v), sz(1), sum(v), ms(v), c(null), rev(false)
{
ch[0] = ch[1] = nil;
pre = suf = max(v, 0);
}
int type()
{
return this == fa->ch[1];
}
void setc(int t, Node* x)
{
(ch[t] = x)->fa = this;
}
void up()
{
Node* lc = ch[0], * rc = ch[1];
sum = lc->sum + rc->sum + v;
ms = max(max(lc->ms, rc->ms), lc->suf + v + rc->pre);
pre = max(lc->pre, lc->sum + v + rc->pre);
suf = max(rc->suf, rc->sum + v + lc->suf);
sz = lc->sz + rc->sz + 1;
}
void reverse()
{
rev = !rev;
swap(ch[0], ch[1]);
swap(pre, suf);
}
void cover(int x)
{
if (this == nil) return;
v = c = x;
sum = x * sz;
ms = x > 0 ? sum : x;
pre = suf = max(sum, 0);
}
void down()
{
Node* &lc = ch[0], * &rc = ch[1];
if (rev) {
lc->reverse();
rc->reverse();
rev = false;
}
if (c != null) {
lc->cover(c);
rc->cover(c);
c = null;
}
}
} * root;
void rot(Node* x, int t)
{
Node* p = x->fa, * y = x->ch[t];
if (p) p->setc(x->type(), y);
else y->fa = 0, root = y;
x->setc(t, y->ch[t^1]);
y->setc(t^1, x);
x->up();
y->up();
}
void splay(Node* x, Node* top)
{
Node* y, * z;
int t1, t2;
for (y = x->fa; y != top; y = x->fa) {
t1 = x->type();
z = y->fa;
if (z != top) {
t2 = y->type();
if (t1 == t2)
rot(z, t2), rot(y, t1);
else
rot(y, t1), rot(z, t2);
} else
rot(y, t1);
}
}
void access(Node* x, int k, Node* r=0)
{
while (true) {
x->down();
if (x->ch[0]->sz + 1 == k)
break;
if (x->ch[0]->sz >= k)
x = x->ch[0];
else {
k -= x->ch[0]->sz + 1;
x = x->ch[1];
}
}
splay(x, r);
}
inline Node* split(int pos, int n)
{
access(root, pos);
access(root->ch[1], n+1, root);
return root->ch[1]->ch[0];
}
inline void update()
{
root->ch[1]->up();
root->up();
}
inline void insert(int pos, Node* x)
{
split(pos+1, 0);
root->ch[1]->setc(0, x);
update();
}
void release(Node* T)
{
if (T == nil) return;
release(T->ch[0]);
release(T->ch[1]);
delete T;
}
inline void erase(int pos, int n)
{
split(pos, n);
release(root->ch[1]->ch[0]);
root->ch[1]->setc(0, nil);
update();
}
inline void cover(int pos, int n, int v)
{
split(pos, n)->cover(v);
update();
}
inline void reverse(int pos, int n)
{
split(pos, n)->reverse();
update();
}
inline int get_sum(int pos, int n)
{
return split(pos, n)->sum;
}
inline int max_sum()
{
return root->ms;
}
Node* build(int* l, int* r)
{
if (l > r) return nil;
int* m = l + (r-l)/2;
Node* T = new Node(*m);
T->setc(0, build(l, m-1));
T->setc(1, build(m+1, r));
T->up();
return T;
}
struct Init {
Init()
{
nil = new Node;
}
} I;
int main()
{
int n, m;
static int A[int(4e6)];
char s[10];
scanf("%d%d", &n, &m);
For (i, 1, n)
scanf("%d", &A[i]);
A[0] = A[n+1] = null;
root = build(A, A+n+1);
while (m--) {
int pos, tot, c;
scanf("%s", s);
if (s[2] == 'X') { // max_sum
printf("%d\n", max_sum());
continue;
}
scanf("%d%d", &pos, &tot);
switch (s[0]) {
case 'I': // insert
Rep (i, 0, tot)
scanf("%d", &A[i]);
insert(pos, build(A, A+tot-1));
break;
case 'D': // delete
erase(pos, tot);
break;
case 'R': // reverse
reverse(pos, tot);
break;
case 'G': // get-sum
printf("%d\n", get_sum(pos, tot));
break;
default: // make-same
scanf("%d", &c);
cover(pos, tot, c);
}
}
return 0;
}