题意:给定n个初始值和一组对区间的操作操作,要求对操作应答。操作包括:1. 修改为指定数。2. 增加一个数。3. 询问区间最大值。4. 询问区间的历史最大值。
《NOI导刊》的培训上,zrt神犇推荐了这道题,说可以加深对lazy tag的理解。乍一看,这不是水题??WA……
网上题解说这是水题,可是我搞了好几天。学习了一下题解,愈发不解。也许还是得自己领悟。在火车上头疼了一天,用光了笔记本的电。有收获。
解
记修改为=,累加为+,则所有操作序列可化为 ++…+==…=的形式,即先进行0个或以上累加操作,再进行0个或以上赋值操作。
如:+4 +5 =2 +(-6) +7 =1 可化为:+4 +5 =2 =-4 =3 =1。
所以,我们用三个lazy tag刻画一组操作:a(累加)和s(赋值),以及和s配套的fs(是否赋值)。若fs=true,以s为准(a会被设为0);否则以a为准。向下传递累加操作的过程中,若孩子的fs=true,累加到s上,否则累加到a上。
从最终的效果来看,上面的例子可再化简为 =1,然而这样会丢失历史信息。向上面那样,保留“一帧帧”操作,便于处理历史最值。
用两个lazy tag刻画历史:ah(历史最大累加)和sh(历史最大赋值)。这里就不再另设赋值与否的标记了。因为,作为历史,即最大值的最大值,赋为-inf是有意义的。前面其实也可以这样,但是意义略微有些不明朗。在上面的例子中,ah=9,sh=3。之所以要记录两个,是因为缺乏加法的另一个操作数,无从确定结果究竟哪个大。如:设原数为0,则累加更大;设原数为-7,则赋值更大。而区间中的数是各式各样的。
来看看两个操作序列的历史如何复合(pushdown时怎样合并)。
++…+==…= x ++…+==…=
ah = ah1
sh = max(sh1, s1+ah2, sh2)++…+ x ++…+==…=
ah = max(ah1, a1+ah2)
sh = sh2
不用进行更多的讨论了,因为add标记有0,set标记有-inf,可以屏蔽自身。需要注意的是,mx是执行了操作序列1后的结果,ah和它并不配套。
添加新标记,也可视为复合。
总结
可以归纳出设计lazy tag的步骤:
1. 用哪些量刻画一组操作?
2. 两个操作序列的结合性是怎样的?
3. 标记的优先级是怎样的?
4. 怎样快速得到操作的等价效果?
误区
- 不加额外的历史标记。这样能过样例……样例看起来很丰富,实际上很水。
- 使赋值的优先级高于累加。会丢失信息。
- 实时标记和历史标记的赋值、累加优先级不一致。不问历史,怎么规定优先级都可以。刘汝佳《算法竞赛入门经典2》里的规定就和这里不同,刚开始我生搬硬套了。隐约意识到问题,却只改了历史标记的优先级。
- 没抓住历史和实时之间的联系。一个很重要的联系是,它们始终是同时传递的。
老毛病了。没搞明白还想A题?
贺神说:慢慢来。
虽然上下文不同,但在这里也是适用的。
我太想AC了。对拍的时候都是心惊胆颤的。
Code
#include <cstdio>
#include <algorithm>
#include <cctype>
using namespace std;
const int MAXT = 100000, NEG_INF = 0x80000000;
int A[MAXT+1];
struct Node {
int mx, mxh, a, ah, s, sh; // 最大值,历史最大值,累加标记,历史累加标记,历史赋值的值
bool fs; // 赋值标记
} T[MAXT*4];
inline void update(int& x, int y)
{
x = max(x, y);
}
inline void maintain(int o)
{
T[o].mx = max(T[o*2].mx, T[o*2+1].mx);
T[o].mxh = max(T[o*2].mxh, T[o*2+1].mxh);
}
inline void push_his(Node& self, Node& fa)
{
update(self.mxh, max(self.mx + fa.ah, fa.sh));
if (self.fs) // +++=== x +++===
update(self.sh, max(self.s + fa.ah, fa.sh));
else { // +++ x +++===
update(self.ah, self.a + fa.ah);
self.sh = fa.sh;
}
}
inline void push_now(Node& self, Node& fa)
{
if (fa.fs) {
self.fs = true;
self.mx = self.s = fa.s;
self.a = 0;
} else {
(self.fs ? self.s : self.a) += fa.a;
self.mx += fa.a;
}
}
inline void pushdown(int o)
{
Node& lc = T[o*2], & rc = T[o*2+1], & self = T[o];
push_his(lc, self);
push_his(rc, self);
push_now(lc, self);
push_now(rc, self);
self.fs = false;
self.sh = NEG_INF;
self.a = self.ah = 0;
}
void build(int o, int l, int r)
{
T[o].sh = NEG_INF;
if (l == r) {
T[o].mx = T[o].mxh = A[l];
return;
}
int m = (l+r)/2;
build(o*2, l, m);
build(o*2+1, m+1, r);
maintain(o);
}
void modify_add(int o, int l, int r, int L, int R, int a)
{
if (L<=l && r<=R) {
Node x = (Node){0, 0, a, a, 0, NEG_INF, false};
push_his(T[o], x);
push_now(T[o], x);
return;
}
pushdown(o);
int m = (l+r)/2;
if (L <= m)
modify_add(o*2, l, m, L, R, a);
if (R > m)
modify_add(o*2+1, m+1, r, L, R, a);
maintain(o);
}
void modify_set(int o, int l, int r, int L, int R, int v)
{
if (L<=l && r<=R) {
Node x = (Node){0, 0, 0, 0, v, v, true};
push_his(T[o], x);
push_now(T[o], x);
return;
}
pushdown(o);
int m = (l+r)/2;
if (L <= m)
modify_set(o*2, l, m, L, R, v);
if (R > m)
modify_set(o*2+1, m+1, r, L, R, v);
maintain(o);
}
void query(int o, int l, int r, int L, int R, int& now, int& his)
{
if (L<=l && r<=R) {
now = max(now, T[o].mx);
his = max(his, T[o].mxh);
return;
}
pushdown(o);
int m = (l+r)/2;
if (L <= m)
query(o*2, l, m, L, R, now, his);
if (R > m)
query(o*2+1, m+1, r, L, R, now, his);
}
int query(int n, int L, int R, int op)
{
int now = NEG_INF, his = NEG_INF;
query(1, 1, n, L, R, now, his);
return op ? his : now;
}
inline int read()
{
int s = 1, x = 0; char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') s = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x*10 + ch - '0';
ch = getchar();
}
return s*x;
}
int main()
{
int t = read();
for (int i = 1; i <= t; ++i)
A[i] = read();
build(1, 1, t);
int E = read();
while (E--) {
char op = getchar();
while (!isalpha(op))
op = getchar();
int x = read(), y = read(), z;
switch (op) {
case 'Q':
printf("%d\n", query(t, x, y, 0));
break;
case 'A':
printf("%d\n", query(t, x, y, 1));
break;
case 'P':
z = read();
modify_add(1, 1, t, x, y, z);
break;
case 'C':
z = read();
modify_set(1, 1, t, x, y, z);
}
}
return 0;
}
到成都了。