线段树之——单点修改

95 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」第14天,点击查看活动详情

单点修改

谈到单点修改,我们就不由得想起线段树中仅有的可以被成为单点的部分——叶子节点

正如你所见,我们修改单点的操作正是对叶子结点进行的。

不过在修改的同时我们还需要一步一步对叶子节点的所属区间进行更新

譬如,现在我修改了编号为5的叶子节点,将它的值由7变为10,即增加了3

树中,叶子节点是线段(56)的孩子结点,因此线段(56)的值也要增加3

同理:线段(56),线段(58),线段(1~8)都要增加3

因为它们的孩子结点值增加了,作为父亲的他们自然要合并孩子的值

上述的这个 孩子修改值,且将改变应用到父亲上的操作就是上篇文章我们讲的上传函数

因此我们的单点修改要这样写

void pushup(int root){tree[root].val=tree[LL(root)].val+tree[RR(root)].val;}
void update(int root,int id,int k){
	if(tree[root].l==id&&tree[root].r==id){tree[root].val=k;return ;}
    int mid=(tree[root].l+tree[root].r)>>1;
    if(id<=mid)update(LL(root),id,k);
    else  update(RR(root),id,k);
    pushup(root);
}

那么基于目的角度,为什么我们在修改完叶子节点(单点)之后还要把值一步步上传给父亲结点呢

首先我们题目中的查找一般是对于一段区间而言,

因此我们在查询时如果能直接给出一段区间值,

而不是再去遍历它的子节点来合并出答案

必然效率高出不少

所以我们一定要修改完单点后,将新的值上传给它的父亲结点

区间修改

明白了上文的单点修改 的话,其实区间修改理解起来并不会太难了。
区间修改分为两个步骤:

  1. 在线段树中找到我们要修改的目标区间;
  2. 修改这一段区间内的所有点。

我们从根节点出发,因为根节点包含所有点或者区间,往下搜索

直到我们当前搜索的区间全都是要修改的元素。

当整个要修改的区间被结点的左区间所包含时,我们就递归到左区间;
当整个要修改的区间被结点的右区间所包含时,我们就递归到右区间;
要么就是要修改区间刚好跨越左区间和右区间: