在上一篇中,我们讨论了线段树的基础用法,其中我们对于线段树的修改,仅仅限制于对于线段树的点的修改,而不是对于某一个一段区间的修改。
那么我们现在来想想如果对于线段树的一段区间来进行修改的话,如果我们还是来对线段树的每一个点都进行修改的话,那么,假设我们需要修改m个点,其时间复杂度岂不是为O(log(n))了,那么我们所需要的少的时间复杂度的要求就没有达成,为此,lazy数组应运而生,来解决这个问题。

首先我们来认识一下lazy:如果当前区间被需要修改的目标区间完全覆盖,打一个标记。如果下一次的查询或者更改包含此区间,那么将这个标记分解,并且传递给其左右儿子。简单的说,延迟标记在我们需要时,才向下传递信息,如果没有用到,则不再进行操作。这个思想使得处理的复杂度依旧保持在O(log2(n))左右,相比朴素算法大大地降低了复杂度。
例如下面:我们修改3到8之间的值

我们所要修改的 3~8 即在 1 ~10 的左子树上有 也在其右子树上有,且没有完全覆盖1 ~ 10的区间,所以进行分别传导。

我们可以发现,在我们传导过程中,到了3这个叶子节点,4~5区间,6 ~ 8区间的时候,和我们需要改变的3 ~ 8的位置相吻合,此时我们对于完全包含的4 ~5区间,6 ~ 8区间不再进行向下传导,在这两个区间的位子记录更改值即可
假设我们需要在3 ~ 8区间中的每一个数字都进行加1操作,那么有如图所示的操作

我们可以看见,lazy操作在经过完全覆盖的一个区间时,就不再进行向下分摊的操作了,其tree=(r-l+1)*lazy,

代码讲解
对于树的建立

void build_date(int l,int r,int rt)
{if(l==r){tree[rt]=a[l];return 0;}int mid = (l+r)>>1;build_date(l,mid,rt<<1);build_date(mid+1,r,rt<<1|1);tree[rt]+=tree[rt<<1]+tree[rt<<1|1];
}

对于线段树的建立我就不多讲,不懂得可以参照我上一篇的线段树基础对照。

核心代码:pushdown_tree

void pushdown_tree(int l,int r,int rt)
{int mid=(l+r)>>1;lazy[rt<<1]+=lazy[rt];lazy[rt<<1|1]+=lazy[rt];tree[rt<<1]+=lazy[rt]*(mid-l+1);tree[rt<<1|1]+=lazy[rt]*(r-mid);lazy[rt]=0;
}

pushdown_tree是线段树lazy操作的核心代码,对于父节点存储的lazy值,在需要访问或者使用其相应子节点的时候进行下放操作,但是,对于pushdown_tree我们不需要单独使用,我们对于pushdown_tree的操作是在在update_tree和query_tree函数的调用中进行的。
在update_tree中,如果不完全包括l,r范围的改变,那么调用pushdown_tree就向下分摊直到完全包括。
在query_tree中,当输出范围不完全包括l,r范围时,那么就调用pushdown_tree向下分摊直到完全包括。

更改函数update_tree的调用:

void update_tree(int l,int r,int x,int y,int rt,int c)
{if(l==x&&r==y){tree[rt]+=(r-l+1)*c;lazy[rt]+=c;return;}pushdown_tree(l,r,rt);int mid=(r+l)>>1;if(mid>y)update_tree(l,mid,x,y,rt<<1,c);else if(mid<x)update_tree(mid+1,r,x,y,rt<<1|1,c);else{update_tree(l,mid,x,mid,rt<<1,c);update_tree(mid+1,r,mid+1,y,rt<<1|1,c);}tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

对于update_tree函数的调用我们需要注意的是的在对左右子树是否都需要递归,当我们需要更改的范围在完全处于当前父节点的左(右)子树上的时候,我们只需要向一边进行递归即可。

查询操作

ll query_tree(l,r,x,y,rt)
{if(l==x&&r==y)return tree[rt];pushdown_tree(l,r,rt)int mid=(r+l)>>1;if(mid<x)return query_tree(mid+1,r,x,y,rt<<1|1);else if(mid>=y)return query_tree(l,mid,x,y,rt<<1);elsereturn query_tree(l,mid,x,mid,re<<1)+query_tree(mid+1,r,mid+1,y,re<<1|1);
}

注意在没有(l ==x&&r= =y)的时候要进行pushdown_tree,对父节点的lazy数组进行向下 释放。

最后就是完整代码的输出

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int maxl=1000;
int a[maxl];
ll tree[maxl*4];//一般对于tree和lazy开四倍就可,很多大佬也喜欢用40,32来运算,也可以
ll lazy[maxl*4];
void build_date(int rt,int l,int r)
{if(l==r){tree[rt]=a[l];return ;}int mid=l+r>>1;build_date(rt<<1,l,mid);build_date(rt<<1|1,mid+1,r);tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void pushdown_tree(int rt,int l,int r)//对于pushdown_tree函数不需要单独使用
//在update_tree中,如果不完全包括l,r范围的改变,那么调用pushdown_tree就向下分摊直到完全包括
//在query_tree中,当输出范围不完全包括l,r范围时,那么就调用pushdown_tree向下分摊直到完全包括
{int mid=l+r>>1;lazy[rt<<1]+=lazy[rt];lazy[rt<<1|1]+=lazy[rt];tree[rt<<1]+=lazy[rt]*(mid-l+1);tree[rt<<1|1]+=lazy[rt]*(r-mid);lazy[rt]=0;
}
void update_tree(int rt,int l,int r,int x,int y,int c)
{if(l==x&&r==y){tree[rt]+=(r-l+1)*c;lazy[rt]+=c;return ;}pushdown_tree(rt,l,r);int mid=l+r>>1;if(y<=mid)update_tree(rt<<1,l,mid,x,y,c);else if(x>mid)update_tree(rt<<1|1,mid+1,r,x,y,c);else{update_tree(rt<<1,l,mid,x,mid,c);update_tree(rt<<1|1,mid+1,r,mid+1,y,c);}tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
ll query_tree(int rt,int l,int r,int x,int y)
{if(l==x&&r==y){return tree[rt];}pushdown_tree(rt,l,r);int mid=l+r>>1;if(y<=mid)return query_tree(rt<<1,l,mid,x,y);else if(x>mid)return query_tree(rt<<1|1,mid+1,r,x,y);elsereturn query_tree(rt<<1,l,mid,x,mid)+query_tree(rt<<1|1,mid+1,r,mid+1,y);
}
int main()
{int n,q;cin>>n>>q;memset(lazy,0,sizeof(lazy));for(int i=1;i<=n;i++)cin>>a[i];build_date(1,1,n);for(int i=1;i<=32;i++)cout<<"tree["<<i<<"]="<<tree[i]<<endl;while(q--){char str[10];cin>>str;if(str[0]=='Q'){int x,y;cin>>x>>y;cout<<query_tree(1,1,n,x,y)<<endl;//遍历与输出函数}else{int x,y,c;cin>>x>>y>>c;update_tree(1,1,n,x,y,c);//改变区域值函数,在[x,y]范围中,每个数字改变c}}return 0;
}

对于线段树的进阶lazy就讲完了,下一期来讲权值线段树和某位大佬发明的主席树

线段树(lazy用法)相关推荐

  1. POJ 3468 线段树+lazy标记

    lazy标记   Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u  Submit S ...

  2. HYSBZ - 1798 Seq 维护序列seq 线段树lazy标记

    传送门 这道题属实是线段树的道比刷题,又加又乘的,当然还可能会有乘除,阶乘等等可能的情况. 对于这道题,主要的一个就是怎么记录lazy标记,首先的话一个数组是肯定不行的,设乘的为lazy,加的为add ...

  3. CodeForces - 817F MEX Queries(线段树lazy序)

    题目链接:点击查看 题目大意:初始时有一个空的集合,需要执行 n 次操作: 1 l r:将区间 [ l , r ] 内未出现的数加入到集合中 2 l r:将区间 [ l , r ] 内出现的数字全部删 ...

  4. 20.CF817F MEX Queries 线段树(Lazy标记练习)

    20.CF817F MEX Queries 离散化+区间覆盖+区间反转线段树 个人Limitの线段树题单题解主目录:Limitの线段树题单 题解目录_HeartFireY的博客-CSDN博客 要求维护 ...

  5. [hiho 22]线段树-lazy标记的下放

    题目描述 之前提到过,线段树之所以更新查询快,是因为区间更新有lazy标记使得不需要每次都操作到叶子节点. 但是如果要操作一个节点时,其父节点上的lazy标记应当被释放,否则该节点无法得到最新的正确结 ...

  6. POJ 3225 线段树+lazy标记

    lazy写崩了--. 查了好久 /* U-> [l,r]–>1 I-> [1,l-1] [r+1,+无穷] –>0 D-> [l,r]–>0 C-> [1,l ...

  7. POJ 3237 Tree (树链剖分 路径剖分 线段树的lazy标记)

    题目链接:http://poj.org/problem?id=3237 一棵有边权的树,有3种操作. 树链剖分+线段树lazy标记.lazy为0表示没更新区间或者区间更新了2的倍数次,1表示为更新,每 ...

  8. hdu 3397 Sequence operation(线段树,lazy,区间合并)

    hdu 3397 Sequence operation 线段树lazy和区间合并结合的一个题,相当于几个题集中到一起嘛,分开想就好了 0,1,2操作都要lazy,2的异或操作找到每一只含1或只含0的区 ...

  9. poj 2985(并查集+线段树求K大数)

    解题思路:这道题并查集很容易,合并时找到父节点就直接加上去就ok了.关键是如何求K大数,我一直在想用线段树怎么写,一开始想如果直接记录数的大小那肯定是没戏了,借鉴了一下别人的思路:区间[a,b]记录的 ...

  10. hdu 3564(线段树+LIS)

    题意:给出1~n的插入顺序,要求每次插入之后的LIS 解题思路:这道题确实挺难想的,我最开始想用树状数组每插入一个数就更新一次,如果这么想,那么你就输了.它这里的想法是先将1-n的最终位置都保存起来, ...

最新文章

  1. Powershell 如何批量获取文件大小的实现代码
  2. 嵌入式系统学习-面试要点总结
  3. From 《visual C++ 6.0开发工具与调试》
  4. linux 五种IO模型 简介
  5. UMDF驱动开发入门
  6. Java基础之String,StringBuilder,StringBuffer三者的区别
  7. python和按键精灵自动化测试_按键精灵对APP自动化测试(下)
  8. 关于android中postDelayed方法的讲解
  9. 机房收费系统的合作版
  10. 音乐服务器 linux,在Ubuntu/Debian/CentOS上安装Koel以配置个人音乐流媒体服务器
  11. java 直播_一对一直播源码开发过程中区分Java和PHP的重要性
  12. dwz框架---(2)表单回调函数
  13. 第四章:mongodb 命令行操作进程控制性能优化
  14. 经典游戏IP:传统端游大厂的成功“捷径” ARPU值更高
  15. 基于STM32的有方科技(银而达)N58模块与OneNet平台互联
  16. 发电子邮件怎么发,手机发电子邮件教程来了
  17. AS使用Viewbinding出现Could not find method buildFeatures() for arguments报错
  18. 申请ios开发者证书到获取p12文件及profiles文件
  19. Linux基础知识总结 一
  20. android 使用icon进行字符编码转换

热门文章

  1. Beyond Compare 报错 This license key has been revoked: 8454-8413
  2. number of lines annotated by git is not equal to number of linus in the file .check file encoding an
  3. excel 常用技巧
  4. 事务四大特性(ACID):原子性、一致性、隔离性、持久性
  5. linux开发员用游戏本吗,为什么很多程序员使用thinkpad而不是同等价位的游戏本呢?...
  6. CSDN 「Markdown」编辑器的优点、不足、使用技巧和新增功能|CSDN编辑器测评
  7. uibot和按键精灵区别_uibot和按键精灵有什么区别?
  8. CRM客户细分的价值
  9. 考研数学第三章复习:曲率、曲率圆、曲率半径
  10. 苹果4s怎么越狱教程_ios9.2怎么越狱 iOS9.2.1完美越狱教程【详解】