线段树?
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
查询 | 修改 | |
---|---|---|
朴素算法 | O(n) |
O(1) |
前缀和 | O(1) |
O(n) |
树状数组 | O(log2 n) |
O(log2 n) |
ST表 | O(1) |
O(log2 n) |
线段树 | O(1) |
O(log2 n) |
单调队列 | O(1) |
或许不能修改 |
线段树模版1
[区间最值,仅满足单点修改]
满足以下特性:
- 树中每一个节点,代表一个区间
- 根节点代表一个完整的期间
[1,n]
- 每个叶子节点代表长度为1的区间
[x,x]
,即区间的一个元素- 对于非叶子节点的每一个节点
[l,r]
,它的左儿子是[l,mid]
,右儿子是[mid+1,r]
。其中mid=(l+r)/2
- 一个完整二叉树中,根节点p的左儿子编号为
p*2
,右儿子编号为p*2+1
。
Code< / >
- 节点声明(区间: l , r)(状态值(最值): dat):
struct data{ |
- 建树(区间: l , r)(状态值(最值)dat):
void build(int p,int l,int r) //p为当前的节点编号,l r 是当前要赋给t[p]的l r |
- 询问某区间[l,r]的最值:
1.该节点全覆盖时不再往下递归,但需返回该节点的值
2.该节点取中点mid后,观察是那一边(中点左右)完全没有覆盖则不再往下递归;否则都需要往下递归(含半边全覆盖,因为下一层会直接返回值)
3.该节点需返回两边递归后处理的结果(当然,如果只有一边递归,就直接返回这一边的值)
int ask(int p,int l,int r) |
- 指定位置x ,修改值为v (递归过程同上):
void change(int p,int x,int v) |
线段树模版2
[满足区间修改] P3372 【模板】线段树 1
带延迟标记功能的线段树
核心代码
- spread(int p)为延迟标记下移一层
注意:当该层打上标记后,表示该层内容已经修改,本层以下等待机会再修改。机会来之下一次修改或查询时经过此节点往下递归时。
void spread(int p) |
推荐题
来做题吧!