线段树
线段树
引入
线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。
线段树可以在 O(log N)的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。 ——oi wiki
基本结构
-
线段树的结构本质上是一个二叉树。
-
每个点记录其所维护的区间值图片
-
设一父节点编号为 \(p\),那么其左子节点为 \(p\times2\),右子节点为 \(p\times 2+1\)。
建树
变量声明:
struct Tree{
ll sum;
ll add;
int l, r;
}tree[500000];
ll a[100005];
递归建树:
void build(int p, int l, int r){
tree[p].l = l;
tree[p].r = r;
tree[p].add=0;
if(l==r){
tree[p].sum=a[l];
return;
}
int mid=(l+r)>>1;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
tree[p].sum = tree[p*2].sum + tree[p*2+1].sum;
}
懒标记
懒标记的作用是记录每次、每个节点要更新的值,也就是 Δ。但线段树的优点不在于全记录,而在于传递式记录。
流程为:
- 更新子节点的lazytag
- 更新当前节点的结果
- 将当前节点的tag初始化
void pushDown(int p){
if(tree[p].add){
//维护两个子节点的lazytag
tree[p*2].add += tree[p].add;
tree[p*2+1].add += tree[p].add;
//利用父节点的tag维护两个子节点的结果
tree[p*2].sum += tree[p].add*(tree[p*2].r-tree[p*2].l+1);
tree[p*2+1].sum += tree[p].add*(tree[p*2+1].r-tree[p*2+1].l+1);
//将父节点的lazytag初始化
tree[p].add = 0;
}
}
更新流程
(以区间加一个数 k 为例)
- 检查当前节点是否完全包含在目标区间内:
- 如果当前节点的区间
[tree[p].l, tree[p].r]完全包含在目标区间[l, r]内,则直接更新当前节点的值:- 更新
sum:sum += k * (区间长度) - 更新
add标记:add += k
- 更新
- 然后返回,不需要继续递归。
- 如果当前节点的区间
- 如果当前节点不完全包含在目标区间内:
- 调用
pushdown,将当前节点的懒标记下传给子节点。 - 递归更新左子节点和右子节点。
- 递归结束后更新当前节点的
sum值。
- 调用
void update(int p, int x, int y, ll k){
int l=tree[p].l, r=tree[p].r;
if(y<l || x>r) return; //检查越界
if(x<=l && y>=r){
//当前节点被包含在目标区域内
//同时也是这一条递推线上的lazytag推到头了,做出与pushdown类似的操作
tree[p].sum += k * (r-l+1);
tree[p].add += k;
//更新完结果和标记后返回
return;
}
//对于大于目标区域的节点:1.下推标记;2.向下递归;
pushDown(p);
update(p*2, x, y, k);
update(p*2+1, x, y, k) ;
//返回后向上更新结果
tree[p].sum = tree[p*2].sum + tree[p*2+1].sum;
}
查询流程
线段树的查询操作是递归地查找与目标区间有交集的节点,并合并结果。
对于查询区间 [L, R]
-
从根节点开始,检查当前节点表示的区间
[left, right]是否与[L, R]有交集。 -
如果没有交集,直接返回无效值(例如,查询区间和时返回
0)。 -
如果当前节点区间完全包含在
[L, R]内,直接返回当前节点的值。 -
否则,递归查询左右子节点,并合并结果(例如,查询区间和时返回左右子节点的和)。
ll request(int p, int x, int y){
ll ans=0;
int l=tree[p].l, r=tree[p].r;
if(y<l || x>r) return 0;
if(x<=l && y>=r){
return tree[p].sum;
}
pushDown(p);
ans += request(p*2, x, y);
ans += request(p*2+1, x, y);
return ans;
}

浙公网安备 33010602011771号