简要题意:
维护一个数组的区间修改,区间查询。
关于区间问题,其实有很多不错的算法。
当然 树状数组 也可以解决,不过为了给 后一道模板 做铺垫,我们将本题作为 线段树 的模板题讲解。
线段树什么?顾名思义,每个节点都维护了一个线段的信息,并且 线段树是一棵完全二叉树 。
比方说:
其中每个节点上写的三个数分别表示:节点编号,区间左端点,区间右端点 。有些节点只写了 节点编号 ,因为它们的 左端点 = 右端点,即元区间,写不下,见谅。
你会发现线段树有这样的性质:
-
i i i 号节点的左儿子编号为 2 × i 2 \times i 2×i(如果存在的话),右儿子编号为 2 × i + 1 2 \times i + 1 2×i+1 (如果存在的话)。其实这是完全二叉树的性质。
-
如果 i i i 号节点维护的区间为 [ l , r ] [l,r] [l,r],则左儿子(如果存在)维护的区间为 [ l , ⌊ l + r 2 ⌋ ] [l,\lfloor \frac{l+r}{2} \rfloor] [l,⌊2l+r⌋],右儿子(如果存在)维护的区间为 [ ⌊ l + r 2 ⌋ + 1 , r ] [\lfloor \frac{l+r}{2} \rfloor + 1 , r] [⌊2l+r⌋+1,r]. 这有利于我们建树。
那么你会说了,嗯,这棵树确实挺不错的,但是能不能维护一些值呢?
——可以,只要是可结合的运算就可以(即可以分裂为两个区间分别计算,再按照某种方式合并的),不一定要满足结合律(比方说区间最大子段和就不满足结合律),比方说 最小值,最大值,区间和,区间异或和,区间最大子段和,区间相邻两数乘积,区间方差等。
你可能不明白是怎么维护的,那我们就样例:
1 5 4 2 3
维护区间和来举例。
此时每个节点从左往右依次为:节点编号,左端点,右端点,区间和。 (不好意思, 9 9 9 号节点的左右端点应该是 [ 2 , 2 ] [2,2] [2,2],口头更正一下)
这时你又发现两个性质:
-
若 i i i 维护的区间 l = r l=r l=r,则 s u m sum sum(区间和) = a l = a r = a_l = a_r =al=ar.
-
若 i i i 维护的区间 l ≠ r l \not = r l=r,则 s u m = l s u m + r