线段树(三)——杨子曰算法

#线段树(三)——杨子曰算法
传送门:线段树(一)
传送门:线段树(二)
传送门:线段树集合


给一段长度为n(n<=200000)的序列,接着是m(m<=20000)个操作对于每个操作显示一个k,如果k=1那么输入一个x,输出第x个元素,如果k=2,那么输入x,y,z,将区间[x,y]的每一个元素都加上z


接着上次的问题,我们开始曰今天的内容,这道题与之前拿那道最大的区别就是它更新的内容是——一个区间,哦,瞬间有大佬发话了,这岂不是很简单,就像上次的区间query一样,分三种情况讨论,全在左区间,全在右区间,以及一半在左,一半在右,然后递归下去就完了,但是有一个很大的区别,就是找到的区间和要更新的区间完全吻合时,不能停止,还要在往下走(因为我对整个区间都要更新,如果不更新,你要输出某个叶子结点时,这个叶子结点还没有更新,于是WA)
呵呵,你以为你想出的是正解,NO!,假设我想在要更新整个区间[1,n],你就得遍历整棵树,复杂度是O(n)的,然后有一个恶心数据m个询问全是更新整个区间,复杂度O(mn),恭喜TLE,正解优化走起


今天,我们来曰一个线段树上的区间更新的优化——lazy标记(懒标记?一个奇怪的名字,到下面就能明白,这个名字是怎么来的),意思就是当找到的区间和要更新的区间完全吻合时——停止,然后在这个结点做一个标记,这个标记就是LAZY标记,呀呀呀,不刚刚说过会WA吗,不要紧张嘛,我们只需要在即将要递归它的儿子的时候,把这个lazy标记下放(←专业人士称之为pushdown),注意,只要需要递归某个结点的儿子,不管你要搞什么事情,update也好,query也好,还是搞笑也好,只要往下走了,就要先把lazy给pushdown掉,懂否?,pushdown走起:

void pushdown(){
	lazy[nod*2]+=lazy[nod];
	lazy[nod*2+1]+=lazy[nod];
	lazy[nod]=0;//灰常重要,千万要把下方的lazy清空
}

对于这道题而言,我们只要记录,某个区间的每个数字的统一变化量就行了(←就是记录lazy)因为它的询问只需要输出某个数的值,最后输出的时候,递归到叶子节点,把这个元素的变化量加上原来的数不久欧了吗?
所以,我觉得不!用!build!(★,°:.☆( ̄▽ ̄)/$:.°★ 。)只要把原来线段树全赋成0就行了(因为开始时lazy都是0),这时的query就跟线段树(二)里那题的update差不多了,都类似一个二分,不多解释,Don‘t 忘记 to pushdown 代码走起:

int query(int l,int r,int k,int nod){
	if (l==r){
		return lazy[nod]+a[k];
	}//到叶子结点
	pushdown(nod);//注意注意注意
	int mid=(l+r)/2;
	if (k<=mid) return query(l,mid,k,nod*2);
	else return query(mid+1,r,k,nod*2+1);
}

来,看update,区间吻合时更新下lazy,结束,有没有觉得更上次的query一样,还是熟悉的三种情况(如果你是鱼的记忆,请往上翻),直接代码走起:

void update(int l,int r,int ll,int rr,int v,int nod){
	if (l==ll && r==rr){
		lazy[nod]+=v;
		return;
	}//区间完全吻合
	pushdown(nod);//不要忘啊不要忘啊不要忘啊
	int mid=(l+r)/2;
	if (rr<=mid) update(l,mid,ll,rr,v,nod*2);//全在左区间
	else if (ll>mid) update(mid+1,r,ll,rr,v,nod*2+1);//全在右区间
	else {
		update(l,mid,ll,mid,v,nod*2)//一半在左
		update(mid+1,r,mid+1,rr,v,nod*2+1)//一半在右
	}
}

OK,完事
好了,到目前为止,你已经能解决大部分的区间线段树问题了
BUT,杨子曰过:“对未知的探索是永无止境滴”
So,线段树(四),预告:


给一个长度为n(n<=200000)的序列,再给出m个操作,对于每个操作,先给出一个k,如果k=1,则输入x,y,输出区间[x,y]的和,如果k=2,则输入x,y,z,把区间[x,y]的每个元素加上z
OK,88
传送门:线段树(四)
于TJQ高层小区
未经作者允许,严禁转载:https://blog.csdn.net/HenryYang2018/article/details/79844739

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值