洛谷 P6109 [Ynoi2009] rprmq1 题解
有一个 \(n \times n\) 的矩阵 \(a\),初始全是 \(0\),有 \(m\) 次修改操作和 \(q\) 次查询操作,先进行所有修改操作,然后进行所有查询操作。
一次修改操作会给出 \(l_1,l_2,r_1,r_2,x\),代表把所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素加上一个值 \(x\)。
一次查询操作会给出 \(l_1,l_2,r_1,r_2\),代表查询所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素的最大值。
对于 \(100\%\) 的数据,\(1\leq n,m\leq 5\times 10^4\),\(1\leq q \leq 5\times 10^5\),\(1\leq x\leq 2147483647\),\(1\leq l_1\leq r_1\leq n\),\(1\leq l_2\leq r_2\leq n\)。
数据结构
线段树
分治
历史最大值
题解
首先,题面提示得很清楚了,直接离线。
我们对于 \(x\) 轴离线,然后动态维护 \(y\) 的值,对于询问,我们可以在每一个 \(x\) 的时候都问一次,然后取最大值,这样就可以过 \(q\) 比较小的情况了。
如果所有的询问的 \(l_1\) 都是 \(1\) 该怎么办呢?我们只要维护历史最大值就可以了。
现在我们的起始端点都是不一样的,该怎么办呢?
可以线段树分治!
于是我们用猫树的技巧,将询问在被 \(mid\) 劈开的这个区间进行计算,然后用 \(l_1 = 1\) 的方法处理 \([l, mid]\) 的后缀答案,\([mid + 1, r]\) 的前缀答案。递归的时候,如果一个修改完全覆盖一个区间就不要下传了,只要提前操作就行了,这样每个修改区间就会被劈成 \(\log\) 个区间,复杂度也有保障了。
最后一个小问题:我们每次要对线段树重置,也就是要使当前的历史最大值不影响到之后的答案,该怎么办?
一种解决办法是每次修改的时候进行备份,然后打标记进行复原。
另一种实现、常数比较好的办法是每次加 \(\infty\)(一个很大但不至于太大的值,比如 \(5 \times 10^{13}\)),然后每次询问的时候减去 \(\infty\),历史最大值就被重置了。
启示
- 如果我们的端点确定了就很好做,而对于端点不确定的情况,我们可以以一个 \(\log\) 的代价使用线段树分治进行优化。
- 这题因为线段树的板子问题(手残了,好久没有板子写挂了)调了半天,下次要注意。