线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。 线段树可以在 O(\log N) 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。
线段树原理
建树
线段树将每个长度不为
的区间划分成左右两个区间递归求解,把整个线段划分为一个树形结构,通过合并左右两区间信息来求得该区间的信息。这种数据结构可以方便的进行大部分的区间操作。
在实现时,我们考虑递归建树。设当前的根节点是p,如果根节点区间管辖的长度已经是1,则可以直接根据a数组上相应位置的值初始化该节点。否则我们将该区间从中点处分割为两个子区间,分别进入左右子节点递归建树,最后合并两个子节点的信息。
| |
| |
查询
如果要查询的区间为[3,5],此时不能直接获取区间的值,但[3,5]可以拆成[3,3]和[4,5],可以合并这两个区间的答案来求得这个区间的答案。
如果要查询的区间是[l,r],则可以将其拆成最多为O(log n) 个极大的区间,合并这些区间即可求出[l,r]的答案。
- c++
| |
- java
| |
修改和懒惰标记
- 懒惰标记
通过延迟对节点信息的修改,从而减少不必要的操作次数。每次执行修改,通过打标记的方法表明该节点对应的区间在某一次操作中被修改,但不更新该节点的字节信息。实质性的修改在下次访问带有标记的节点时才进行。
- 修改
| |
| |
- 查询
| |
| |
- 修改为某个值
| |
| |
动态开点线段树
前面的堆式存储情况下,需要给线段树开4n大小的数组。为了节省空间,我们可以不一次性建好树,而是在最初只建立一个根节点代表整个区间。当我们需要访问某个子区间时,才建立代表这个区间的子节点。这样我们不再使用2p和2p+1代表p节点的儿子,而是使用ls和rs来记录儿子的编号。
节点只有在有需要的时候才被创建
单次操作的时间复杂度不变,为log(n)。由于每次创建操作都有可能创建并访问全新的一系列节点,因此m次单点操作后节点的数量规模是mlog(n)。最多只需要2n-1个节点,没有浪费。
- 修改
| |
| |
- 查询
| |
| |
优化
- 在叶子节点处无需放下懒惰标记,所以懒惰标记可以不下传到叶子节点。
- 下放懒惰标记可以写一个专门的函数,从儿子节点更新当前节点也可以写一个专门的函数,降低代码编写难度。
- 标记永久化:如果确定懒惰标记不会在中途加到溢出(超出该类型的最大范围),那么就可以将标记永久化。标记永久化可以避免下传懒惰标记,只需要在进行询问时把标记的影响加到答案当中,从而降低程序常数。
模板
- 区间加/求和
| |
| |
- 区间修改/求和的线段树
| |
| |
例题
P3372 【模板】线段树 1
题目描述
如题,已知一个数列 ${a_i}$,你需要进行下面两种操作:
- 将某区间每一个数加上 $k$。
- 求出某区间每一个数的和。
输入格式
第一行包含两个整数 $n, m$,分别表示该数列数字的个数和操作的总个数。
第二行包含 $n$ 个用空格分隔的整数 $a_i$,其中第 $i$ 个数字表示数列第 $i$ 项的初始值。
接下来 $m$ 行每行包含 $3$ 或 $4$ 个整数,表示一个操作,具体如下:
1 x y k:将区间 $[x, y]$ 内每个数加上 $k$。2 x y:输出区间 $[x, y]$ 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。
输入输出样例 #1
输入 #1
| |
输出 #1
| |
说明/提示
对于 $15%$ 的数据:$n \le 8$,$m \le 10$。
对于 $35%$ 的数据:$n \le {10}^3$,$m \le {10}^4$。
对于 $100%$ 的数据:$1 \le n, m \le {10}^5$,$a_i,k$ 为正数,且任意时刻数列的和不超过 $2\times 10^{18}$。
【样例解释】

| |
P3373 【模板】线段树 2
题目描述
如题,已知一个数列,你需要进行下面三种操作:
- 将某区间每一个数乘上 $x$;
- 将某区间每一个数加上 $x$;
- 求出某区间每一个数的和。
输入格式
第一行包含三个整数 $n,q,m$,分别表示该数列数字的个数、操作的总个数和模数。
第二行包含 $n$ 个用空格分隔的整数,其中第 $i$ 个数字表示数列第 $i$ 项的初始值。
接下来 $q$ 行每行包含若干个整数,表示一个操作,具体如下:
操作 $1$: 格式:1 x y k 含义:将区间 $[x,y]$ 内每个数乘上 $k$
操作 $2$: 格式:2 x y k 含义:将区间 $[x,y]$ 内每个数加上 $k$
操作 $3$: 格式:3 x y 含义:输出区间 $[x,y]$ 内每个数的和对 $m$ 取模所得的结果
输出格式
输出包含若干行整数,即为所有操作 $3$ 的结果。
输入输出样例 #1
输入 #1
| |
输出 #1
| |
说明/提示
【数据范围】
对于 $30%$ 的数据:$n \le 8$,$q \le 10$。
对于 $70%$ 的数据:$n \le 10^3 $,$q \le 10^4$。
对于 $100%$ 的数据:$1 \le n \le 10^5$,$1 \le q \le 10^5$。
除样例外,$m = 571373$。
(数据已经过加强 ^_^)
样例说明:

故输出应为 $17$、$2$($40 \bmod 38 = 2$)。
| |


