线段树之———区间修改

83 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」第15天,点击查看活动详情 上篇文章我们讲了,区间修改包括两个步骤,

一是找区间,而是将满足条件的点全部修改

且找区间的时候我们需要注意一下几点:

倘若是当被修改的区间完全在结点的左区间或者右区间时,

很简单,我们递归到结点的左孩子或右孩子去找答案就行

if(r<=mid) update(root<<1,l,r,k); //如果被修改区间完全在结点左区间
else if(l>mid) update(root<<1|1,l,r,k);//如果被修改区间完全在结点右区间

如果是当&&那么我们就要找的区间分为两份去寻找

else update(root<<1,l,mid,x),update(root<<1|1,mid+1,r,x);

ok现在逻辑理好了,那我们来想想怎么进行修改,

最符合正常想法的是找到被区间包含的叶子结点,逐一修改

但如果是这样,效率不仅不比线性数组强,甚至更糟糕了

不妨想想:倘若目标需要修改区间,将区间里的元素改为3,如果你在搜索区间过程中,

发现了当前搜索的区间就已经是了,那咱还需要递归到最底层的叶子节点吗?

肯定是多此一举的,我们只需要修改当前区间,传递修改为3这个标记给孩子子节点 即可


因为倘若某次查询到了该层的子节点的值,那么我们肯定是要先将结点值修改为3,再返回结果,

不必用到结点时我们就只要 保留这个操作标记给孩子 就好,来节省时间。


这个标记我们称之为懒惰标记(lazy)


在修改过程中,我们搜索目标区间时,每搜寻到一个区间,要先判断一下是否之前有标记

如果有要更新该区间结点的值且将标记向下传递给孩子结点


如下展示的是加法懒惰标记的修改使用

void pushdown(ll root) {
	if (!tree[root].lazy)return;
	tree[LL(root)].zhi += tree[root].lazy* (tree[LL(root)].r - tree[LL(root)].l + 1);
	tree[RR(root)].zhi += tree[root].lazy* (tree[RR(root)].r - tree[RR(root)].l + 1);
	tree[LL(root)].lazy += tree[root].lazy;
	tree[RR(root)].lazy += tree[root].lazy;
	tree[root].lazy = 0;
}
void updata(ll root, ll l, ll r,ll k) {
	pushdown(root);
	if (tree[root].l > r || tree[root].r < l)return;
	if (l <= tree[root].l&&tree[root].r <= r) { tree[root].zhi += k*(tree[root].r-tree[root].l+1), tree[root].lazy += k; return; }
	updata(LL(root), l, r,k),updata(RR(root),l,r,k);
	pushup(root);
}