洛谷 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\) 的代价使用线段树分治进行优化。
  • 这题因为线段树的板子问题(手残了,好久没有板子写挂了)调了半天,下次要注意。

  代码

  

posted @ 2022-01-10 15:41  Werner_Yin  阅读(113)  评论(0编辑  收藏  举报