Segment Tree 线段树笔记

算法描述
线段树常用于解决区间修改查询问题,包括但不限于最大值、最小值和求和。线段树最大的难点,就是你不知道它将用来解决怎样的问题。
定义
线段树就是一棵普普通通的树,它的大部分节点都有两个子节点,但并不是一棵满二叉树,也不是完全二叉树。这一点你在学习后面的文章后就会明白了。
线段树上每一个节点都代表着一段区间,而每个子结点代表的区间长度都是其父节点的一半。若长度是奇数则会有些许偏差。而节点的值则是这个区间的需求。
应用
线段树可以解决大部分的区间问题,只要你能想清楚怎么分治,基本上就是线段树没得跑。哪怕这个区间的需求是连续的。
通常来说,线段树的操作分为以下几种:
- 建树:输入数组并建立对应需求的线段树
- 单点修改:将
位置的元素改成 - 区间修改:将
到 位置的所用元素改成或增加 - 区间查询:查询
到 区间内的最大/最小/和/其他需求
算法实现
线段树用数组实现。注意数组大小要开到
1 | ll lc(ll p){return p << 1;} |
初始化
从根节点开始,不断的分割区间直到该区间只剩单个数,然后开始向上汇总。传入两个变量 l
和 r
, 表示当前节点表示的线段。
1 | void pushUp(ll p){ |
区间查询
从根节点开始,如果当前的线段不被查询的区间完全包含就一直分两段尝试,直到它被完全包含了,返回该线段的值然后继续递归其他的。
1 | ll Query(ll p, ll l, ll r, ll ql, ll qr){ |
区间修改
如果每次修改都从上到下全改一遍,复杂度得
1 | void Update(ll p, ll l, ll r, ll ql, ll qr, ll d){ |
例题
例题:Luogu P3372
1 |
|
变种
区间和并
在遇到有区间连续的需求时,我们发现传统线段树并不适用,因为分段时无法控制答案的范围,在向上整理回父节点时可能会遇到答案的中间部分没有连接但却错误转移的情况。
我们可以定义额外的变量存于线段树的数组,分别表示了以区间最左边和最右边起始的答案,这样就只需要思考如何 pushUp
转移了。
例题
例题:区间最大连续子序列和
输入一个长度为
1 x v
,该指令将的值修改为 2 l r
,该指令查询区间到 的最大连续子序列和,即区间所有可能的连续子序列中求和的最大值
1 |
|
总结
极为常用,需要会用、会推、会写且达到熟练的地步。复杂度
- Title: Segment Tree 线段树笔记
- Author: Allen2010
- Created at : 2025-03-22 00:00:00
- Updated at : 2025-04-06 20:02:51
- Link: https://allen2010.github.io/2025/03/22/Segment Tree/
- License: This work is licensed under CC BY-NC-SA 4.0.