开启掘金成长之旅!这是我参与「掘金日新计划 · 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);
}