线段树是一个能在 $O(logn)$ 的时间复杂度内进行区间修改和区间查询操作的数据结构。非常神奇!
建树操作
考虑如何构建一颗线段树:
我们对于一个区间 $[l, r]$ ,我们将其分为两个子区间 $[l, mid]$ 和 $[mid + 1, r]$ ,其中 $mid = \frac{l + r}2$ ,对于每一个区间采用递归的方式去建树,知道区间长度为 $1$ ,则将其作为叶子节点(具体参考代码)。
线段树的思想为我们在修改的同时维护每一个子区间,由子区间合并为一个大区间,所以支持区间快速合并的即可用线段树维护。其体现了一个均摊的思想。
对于代码会在最后一起给出。
Lazy-tag
非常明确地,如果是单点修改,我们只需要一直向上递归修改即可,但对于区间修改,如果我们采用暴力一点一点修改的话时间复杂度为 $O(nlogn)$,不如暴力……
我们似乎需要懒一点,不要修改的那么勤奋,最好是仅仅先修改需要用到的就可以了,所以,我们非常机制地引入了一个 Lazy-tag 的思想。
对于 Lazy-tag 即对于每一个节点多维护一个 tag 变量,假如说我们有一个节点维护的区间为 $[a, b]$ ,那么当我们需要修改整个 $[a, b]$ 的信息,如将区间 $[a, b]$ 的数全部加 $c$ ,我们可以暂时仅仅修改维护区间 $[a, b]$ 的这一个节点,暂时忽略其子区间的节点,同时我们给维护区间 $[a, b]$ 的节点上打一个 tag ,即说明此节点包含的区间被整体增加了 tag ,方便日后查询其子区间时修正,然后把维护区间 $[a, b]$ 节点的 sum 直接增加 $c * (b - a + 1)$ 即可。
区间查询
区间查询不多说了,递归查询子区间即可
另外需要注意在查询或修改到一个节点时必须将标记进行下放,即 pushdown 操作。
1 | struct Sgt{ |
To be continued…