Problem5740--学习系列——线段树 VIII —— 区域更新

5740: 学习系列——线段树 VIII —— 区域更新

[Creator : ]
Time Limit : 1.000 sec  Memory Limit : 256 MiB

Description

【知识点:Lazy 思路】
我们没有办法直接循环调用单点更新来实现区域更新,因为这样代价太大。

增量记录

现在有一个数组为 $[1,\ 8,\ 3,\ 4,\ 7,\ 1,\ 6,\ 2]$,我们需要对线段树进行一个区间更新的操作,将 $[1,\ 6]$ 这个区间上的所有数字增加 $4$。此时我们的增量数组如图所示:

为什么要单独用一个属性来记录增量?因为我们上面的图片发现我们需要查的 $[1,\ 6]$ 这个区间,其最小颗粒度的查询节点是 $[1,\ 4]$ 和 $[5,\ 6]$ 这两个节点,所以我们将增量向下更新到这两个节点,就能保证我们查询 $[1,\ 6]$ 这个区间的正确性。
于是,通过这个思路我们来思考,当我们需要对一个区间进行批量增减操作的时候,我们只要向下更新到我们所有查询操作的最小粒度即可,而不用完全对整个线段树进行更新,是不是就完成了复杂度的优化!
这就是 $O(logN)$ 级别的批量更新思路,这就是算法中的“惰性”(Lazy)思想。

Push Down 操作

Push Down 也就是向下更新的意思。因为我们要引入这个增量的记录数组,所以我们需要 Push Down 操作。

在 Push Down 操作中,我们已经保证了这个更新是最小的可查询的粒度。那么,如果我们在后面要在后面去查询更细的粒度,我们要怎么办呢?其实,思路很简单,当我们查询的时候,也执行 Push Down 按照之前需要更新的范围继续向下更新,是不是就可以了。
同样的,由于 Update 区间操作也需要想查询一样最小的可更新粒度,所以我们在每查询到一个节点时,也对其增加一个 Push Down 操作,如此可以保证下方节点都是最新的更新态。

【实现】
在算法实现上,我们将增加一个 push() 函数,该函数用于实现 Lazy 算法。在每个 update() 和 query() 函数中增加对 push() 的调用。这样来实现完整的 Lazy 思想。

Input

第一行包括 $1$ 个整数 $n\ (1≤n≤2*10^5)$,表示数组 $a$ 的大小。
第二行包括 $n$ 个数字,每个数字 $−10^4≤a_i≤10^4$。两个数字用空格隔开。
第三行包括 $1$ 个整数 $m\ (1 \leq m \leq 2*10^5)$,表示 $m$ 个区域更新操作。
第 $4 \sim 4+m-1$ 行,每行是一个操作。操作分为两类:
  • 操作 $1$:1 l r,表示查询区间 $[l,r]$ 的和。
  • 操作 $2$:2 l r c,表示将区间 $[l,r]$ 的数据都加上 $c$。

Output

若干行,每行包括 $1$ 个整数,对于每个操作 $1$,输出一个 $[l,r]$ 的和。

Sample 1 Input

6
1 3 5 7 9 11
15
1 1 6
2 1 1 4
1 1 6
2 3 6 -1
1 1 6
1 1 4
2 1 6 10
1 1 6
2 4 5 3
1 4 5
2 2 3 -5
1 2 3
1 1 6
2 5 6 7
1 5 6

Sample 1 Output

36
40
36
18
96
40
17
92
55

Sample 2 Input

5
1 5 4 2 3
5
1 2 4
2 2 3 2
1 3 4
2 1 5 1
1 1 4

Sample 2 Output

11
8
20

Source/Category

数据结构 2.9.线段树