线段树转载记录
0x01 种一颗线段树
假如我现在有一个数组A,总共有n个元素,我既想实现区间修改,又想单个修改,这时候种一颗线段树来维护这个数组,能平衡收益。
0x02 怎么建立?
线段树是一颗平衡二叉树,双亲结点是区间的和,左右子节点是双亲区间分两半的区间
每个节点 p 的左右子节点的编号分别为 2p 和 2p+1,假设 双亲结点p 的为区间 [l , r]的和。
设 mid = [(l + r)/2],那么两个子节点分别存储[l , mid] 和 [mid+1 , r]的和。可以发现,左节点对应的区间长度,与右节点相同或者比之恰好多1。
知道这些后,便可以用递归的方法来种一颗线段树了。大体的思想就是,从叶子结点开始,从下往上逐步建立。这便是递归的思想
1 | void build(elem l = 1, elem r = n, elem p = 1){ |
0x03 修改维护
在讲区间修改前,要先引入一个 “懒标记”(lazy) 的概念。懒标记是线段树的精髓所在。对于区间修改,朴素的想法是用递归的方式一层层修改(类似于线段树的建立),但这样的时间复杂度比较高。使用懒标记后,对于那些正好是线段树节点的区间,我们不继续递归下去,而是打上一个标记,将来要用到它的子区间的时候,再向下传递。
给目标区间[l , r]加上数d。
1 | void update(ll l, ll r, ll d, ll p = 1, ll cl = 1, ll cr = n) |
分三种情况处理,当前结点p的区间[cl , rl],与目标区间无交集,包含关系,有交集。
用懒标记来给维护区间的加减
0x04 区间查询
1 | ll query(ll l, ll r, ll p = 1, ll cl = 1, ll cr = n) |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 北海の小站!