P4314 CPU监控
线段树标记好题
这题要支持区间加,区间赋值,区间查最大值,区间查历史最大值。
直接用线段树维护区间最大值和历史最大值,再打俩 \(tag\) 是不够的,因为俩 \(tag\) 不一定能够及时下传至叶子节点。
因此我们需要额外多打俩 \(tag\):(从上次更新以后)加法标记的历史最大值,(从上次更新以后)赋值标记的历史最大值。并且我们保证赋值操作绝对优于加法操作,即原有加法标记,再赋值以后会取消加法标记(但是不会清空加法标记的历史最大值);原有历史标记,再加法就直接视为赋值为某个值的操作。
因此,我们在操作标记的时候要对有赋值标记的情况进行特殊处理。
需要注意的是,下放标记的时候,我们需要先下放加法标记,再下放赋值标记,因为加法标记的历史最大值记录的是上次更新之后到第一次赋值标记之前的所有加法的最大值,我们将用 \(now~max + history~add~tag\) 来更新 \(history~max\),而这个 \(now~max\) 应该是上一次更新之后的 \(now~max\),如果先用赋值标记更新的话,\(now~max\) 信息就会出错。
赋值标记不需要管 \(now~max\) 是否正确,因为用不到。
关键代码:
//vst : 是否存在赋值标记
inline void push_set(int cur, int v, int hv) {
if (!cur) return ;
mx[cur] = v, MAX(hmx[cur], hv);
if (vst[cur])
MAX(hstag[cur], hv), stag[cur] = v;//Attention!!
else vst[cur] = true, stag[cur] = v, hstag[cur] = hv;
atag[cur] = 0;
}
inline void push_add(int cur, int v, int hv) {
if (!cur) return ;
MAX(hmx[cur], mx[cur] + hv), mx[cur] += v;//Attention!!! <-error
if (vst[cur]) push_set(cur, stag[cur] + v, stag[cur] + hv);
else MAX(hatag[cur], atag[cur] + hv), atag[cur] += v;
}
inline void pushdown(int cur) {
if (vst[cur] && atag[cur]) exit(-1);
push_add(ls[cur], atag[cur], hatag[cur]),
push_add(rs[cur], atag[cur], hatag[cur]),
atag[cur] = hatag[cur] = 0;
if (vst[cur]) {
push_set(ls[cur], stag[cur], hstag[cur]),
push_set(rs[cur], stag[cur], hstag[cur]),
stag[cur] = hstag[cur] = 0;
vst[cur] = 0;
}
}