线段树总结
参考题目:A Simple Problem with Integers POJ-3468
距离上一次写这道题已经过去两个月了,前天打模拟赛时连线段树都手敲不出来了。所以这次重新来复习一下线段树。
这次主要是记录一下对线段树区间修改的理解:
一开始我们先是理解线段树的建树原理以及查询原理。利用一个二叉树建立,每个父节点可以记录左右子结点的和或者最大值,以此来维护区间内容。
查询也类似,通过递归寻找,找到所有符合条件(区间内深度最浅)结点,然后将其求和返回或者求最大值返回。
而在之后有单点修改。单点修改重建树来理解,就是递归,找到范围(l,r) ,l=r 时的结点位置更新并 push_up() 向上建树。
但是对于大量数据查询,以及面对区间修改问题时,要一个一个找到位置再更新push_up()就会非常费时间。
所以我们试想能否使用一个标记 tag 传递,在更新范围内的结点就把 tag 传递下去,然后对所有含有tag 的叶子结点更新,再push_up();
但是对于上面实际上还有更加优化的方案:即 lazy[] 标记。我们用 lazy[]标记记录每个结点修改信息,就像上面一样。但是当递归到某个完全被包含于查询区间的 部分时,我们直接对这个区间更新 即 (r-l+1) * lazy[p] . (完全被包含,所以直接乘以长度即可),单接下来我就直接返回了,不往下递归了。那没有被更新的那些子结点怎么办?
既然叫 lazy[] 就是真的懒的意思。如果你下次查的区间不包含我要更新的所有叶子结点(或者没有被更新到的子结点部分),那我直接利用已有的信息就可以返回你要的答案。
如果你查的区间包含的话,那我就在查询的同时把那些子结点更新掉,利用我之前 残留下没有更新的 lazy[] 值。所以这样大大减少了修改的时间复杂度。
code:
#include <algorithm> #include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <vector> #include <stack> #include <queue> #include <set> #include <map> #include <complex> #define IOS ios::sync_with_stdio(0); cin.tie(0); #define mp make_pair #define Accept 0 using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const double Pi = acos(-1.0); const double esp = 1e-9; const int inf = 0x3f3f3f3f; const int maxn = 5e5+7; const int maxm = 1e6+7; const int mod = 1e9+7; int n,m; struct segmentTree { ll arr[maxn]; ll tree[maxn<<2]; ll lazy[maxn<<2]; //结点从1开始到n void init(int n){ for(int i=1;i<=n;i++){ scanf("%lld",&arr[i]); } } void build(int l,int r,int p){ lazy[p] = 0; if(l==r) { tree[p] = arr[l]; return ;} int mid = (l+r)>>1; build(l,mid,p<<1); build(mid+1,r,p<<1|1); sum_up(p); } void sum_down(int p,int m){ if(lazy[p]){ lazy[p<<1] += lazy[p]; lazy[p<<1|1] += lazy[p]; tree[p<<1] += (ll)lazy[p]*(m-(m>>1)); tree[p<<1|1] += (ll)lazy[p]*(m>>1); lazy[p] = 0; } } void sum_up(int p){ tree[p] = tree[p<<1] + tree[p<<1|1]; } void update(int ql,int qr,int k,int l,int r,int p){ //完全包含区间,直接返回就不更新其子结点了 //直到下次需要子结点时才更新 if(ql<=l&&r<=qr){ tree[p] += (ll) (r-l+1) * k; lazy[p] += k;//用lazy[p]暂时保存所有跟新值(次数) return ; } sum_down(p,r-l+1); int mid = (l+r)>>1; //部分包含左区间 if(ql<=mid) update(ql,qr,k,l,mid,p<<1); //部分包含右区间 if(qr>mid) update(ql,qr,k,mid+1,r,p<<1|1); sum_up(p); } ll query(int ql,int qr,int l,int r,int p){ ll res = 0; if(ql<=l&&r<=qr){ return tree[p]; } sum_down(p,r-l+1); int mid = (l+r)>>1; if(ql<=mid) res += query(ql,qr,l,mid,p<<1); if(qr>mid) res += query(ql,qr,mid+1,r,p<<1|1); return res; } }seg; int main(){ scanf("%d %d",&n,&m); seg.init(n); seg.build(1,n,1); string s; for(int i=0;i<m;i++){ cin>>s; if(s[0]=='Q'){ int L,R; scanf("%d %d",&L,&R); printf("%lld\n",seg.query(L,R,1,n,1)); }else{ int L,R,k; scanf("%d %d %d",&L,&R,&k); seg.update(L,R,k,1,n,1); } } return 0; }
原文地址:https://www.cnblogs.com/Tianwell/p/11494670.html
- Highcharts AJAX JSON JQuery 实现动态数据交互显示图表 柱形图
- 企业支付宝账号开发接口教程--JAVA-UTF-8(实际操作------SpringMVC+JSP)
- 用pandas 进行投资分析
- 【专业技术】android 应用程序如何获取root权限
- Nginx+Keepalived(双机热备)搭建高可用负载均衡环境(HA)
- SpringMVC+MongoDB+Maven整合(微信回调Oauth授权)
- ZeroClipboard实现多个浏览器兼容的复制文本到剪贴板的功能
- Shiro 权限框架使用总结
- Apriori算法介绍(Python实现)
- linux学习第六十二篇:添加自定义监控项目,配置邮件告警,测试告警,不发邮件的问题处理
- Entity Framework Core 2.0 入门
- Nodejs开发框架Express3.0开发手记–从零开始
- 使用 nvm 管理不同版本的 node 与 npm
- svg矢量图绘制以及转换为Android可用的VectorDrawable资源
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Ubuntu 16.04/18.04 安装Pycharm及Ipython的教程
- linux系统对外开放3306、8080等端口,防火墙设置详解
- Linux中selinux基础配置教程详解
- Linux中如何查看已挂载的文件系统类型详解
- 在 Linux 命令行中使用 tcpdump 抓包的一些功能
- CentOS平台快速搭建LAMP环境的方法
- Linux系统中时间的获取和使用
- 基于Linux搭建Apache网站服务配置详解
- CentOs下手动升级node版本的方法
- 详述Linux中Firewalld高级配置的使用
- CentOS7安装PHP7 Redis扩展的方法步骤
- centos7下rsync+crontab定期同步备份
- 你可能不知道的一些linux文件权限管理方法
- 原来SqlSession只是个甩手掌柜?真正干活的是谁?
- Linux下的定时任务和延时任务的详解