P6109 [Ynoi2009] rprmq1

题面

有一个 \(n \times n\) 的矩阵 \(a\),初始全是 \(0\)

先进行 \(m\) 次修改操作,一次修改操作会给出 \(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\)

在进行 \(q\) 次查询操作,一次查询操作会给出 \(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}\) 元素的最大值。

数据范围:\(n,m\le 5\times 10^4,q\le 5\times 10^5\)

题解

先有暴力做法:

离线下来,然后扫描线,支持区间加减,此时维护的就是 \(i=x\) 的一竖列的值的情况。

但是题目要我们求的是矩形的最大值,也就是随着 \(i\) 从左到右一段时间内的区间最大值。

这看起来很难做,我们可以放宽限制,考虑如果是一段前缀可以做吗?

诶,这不就是线段树区间历史最值吗?

现在回到原情况,现在相当于我们可以从某个点开始,求一段前缀、后缀的的历史最大值。

想到了什么?

分治!

我们把询问分治到 \(l\le mid\le r\) 的一个节点上,然后对每个节点从 \(mid\) 扫到 \(l\) 记录答案,然后撤回,在从 \(mid+1\) 扫到 \(r\) 并记录答案。

但是!我们发现并不是直接扫就能得到答案的,因为对于时刻 \(i\) 一竖列的值的情况是操作的前缀和!所以我们必须安排一个合适的扫描+回退的顺序。

考虑处理 \(l,r\) ,并且当指针位于 \(l-1\) (指针的位置表示处理到的位置) ,我们希望处理完之后退回到 \(l-i\) ,操作如下:

  • 指针扫到 \(mid\)
  • 对历史最值初始化。
  • 扫到 \(r\) ,更新答案。
  • 回退到 \(mid\)
  • 递归处理 \(mid+1,r\) ,处理完指针还在 \(mid\)
  • 对历史最值初始化。
  • 扫到 \(l-1\) ,更新答案。

注意对历史最值初始化怎么做?

  • 方法一:

    可以在线段树上额外开一个标记表示是否要初始化,初始化后会让 \(hma=ma,hbj=-\inf\)

    注意在给 \(p\) 打标记的时候要先 down(p) ,否则就会出现 \(p\) 的标记没有先下传!

    代码上是这样的:

    	void chuli2(int p){
    		chuli1(p<<1,tr[p].bj1,tr[p].bj2),chuli1(p<<1|1,tr[p].bj1,tr[p].bj2);
    		tr[p].bj=1,tr[p].hma=tr[p].ma,tr[p].bj2=inf,tr[p].bj1=0;
    	}
    
  • 方法二:

    可以给线段树整体加一个比较大的数(防止爆long long),这样历史就更新了。

启发

  • 分治的新方式!
  • 历史最值在处理二维最值上的作用!
posted @ 2022-08-03 22:30  qwq_123  阅读(64)  评论(0编辑  收藏  举报