SPOJ 1557 GSS2 - Can you answer these queries II (线段树+维护历史最值)
都说这题是 GSS 系列中最难的,今天做了一下,名副其实
首先你可以想到各种各样的在线乱搞想法,线段树,主席树,平衡树,等等,但发现都不太可行。
注意到题目也没有说强制在线,因此可以想到离线地去解决这道题。
我们把询问按照右端点从小到大排序。假设当前询问的右端点为 \(i\)。
定义 \(s_j\) 为 \([j,i]\) 中不重复数字的和。我们建一棵线段树维护 \(s_j\) 的最大值。
这样修改起来就比较自然了,当右端点从 \(i-1\) 变到 \(i\) 的时候,记 \(a_i\) 上一次出现的位置为 \(k\),那么这个 \(a_i\) 只会对 \(s_{k+1},s_{k+2},\dots,s_i\) 产生贡献,也就是说我们在线段树上 \([k+1,i]\) 位置上的数 \(+a_i\)。
询问也比较容易。询问 \((l,i)\) 的答案就是 \(s_l,s_{l+1},\dots,s_i\) 的历史最大值。
这样原题就转化为一道看起来比较可做的 DS 了,要你维护一个序列,支持:
- 区间加
- 区间历史最大值
之前写过一道类似的题题号什么我忘了,可现在忘了怎么做了/kk,只好看题解再推一遍。
线段树区间维护四个东西 \(mx,hmx,lz,hlz\) 表示最大值,历史最大值,区间加标记,历史最大区间加标记(历史出现过的懒标记的最大值)。
上推操作就不用说了吧,直接对 \(mx,hmx\) 取 \(\max\)。。。。。。
区间加上一个值 \(v\),那么最大值肯定也会加上 \(v\),历史最大值就和当前最大值取 \(\max\),懒标记也同理。
最复杂的就是如何下传标记。
例如我们现在有一段操作序列 \(+1-3+4-2-1+5-2\),那么此时懒标记为 \(+2\),历史最大懒标记为 \(+4\)(\(+1-3+4-2-1+4\))。
然后它的儿子当前最大值为 \(2\),历史最大值为 \(3\),当前懒标记为 \(-1\),历史最大懒标记为 \(+1\)。
那么:
- \(mx\) 显然直接加上父亲的懒标记即可,\(2+1-3+4-2-1+5-2=2+2=4\)。
- \(hmx\) 拿当前最大值 \(2\) 加上历史最大懒标记 \(+4\)(\(2+1-3-4-2-1+5=6\)),与原来历史最大值 \(3\) 比较。
- \(lz\) 也是直接加上父亲的 \(lz\) 即可,\(-1+2=+1\)
- \(hlz\) 也拿当前最大懒标记加上历史最大懒标记 \(+4\)(\(-1+1-3-4-2-1+5=+3\)),与原来历史最大懒标记 \(+1\) 比较。
最后,重中之重,下放懒标记的顺序一定要注意。