线段树 - 从入门到入土

news2024/9/29 1:30:55

普通线段树

线段树是什么

我们要学习线段树,首先要了解线段树的结构长什么样。

线段树是一颗二叉树,树上的节点储存数据(可以是值、字符串、数组、多个值)。

作用

一般来说,线段树是用来维护一个数组的。

数据储存

线段树每个节点上储存数组上区间 [ l , r ] [l, r] [l,r] 的相应数据,根节点储存整个数组的数据。

每个节点的左右儿子分别储存区间 [ l , ⌊ l + r 2 ⌋ ] [l, \left\lfloor\frac{l+r}{2}\right\rfloor] [l,2l+r] [ ⌊ l + r 2 ⌋ + 1 , r ] [\left\lfloor\frac{l+r}{2}\right\rfloor+1,r] [2l+r+1,r] 的数据。

一直分割到叶子节点,所以每个叶子节点上 l = r l = r l=r,只储存数组上一个位置的数据。

一般情况下,节点 o o o 的值储存在下标 o o o 的位置,节点 o o o 的左儿子为节点 o × 2 o\times2 o×2,右儿子为 o × 2 + 1 o\times2+1 o×2+1

但有时则自由定义节点 o o o 的左右儿子,并另行记录。

具体操作

单点修改区间查询

有一个长度为 n n n 的数组 a a a,有 m m m 个操作

  1. 询问 a a a [ s , t ] [s, t] [s,t] 的和
  2. a x a_x ax 的值加 v v v

1 ≤ n , m ≤ 1 0 6 1\leq n, m \leq 10^6 1n,m106

单点修改

修改单个点,代表着我们需要修改线段树上所有包括这个点的区间。

很显然,如果一个更大的区间 [ l , r ] [l, r] [l,r] 不包括 x x x,那么这个区间的所有子区间都不包括 x x x

所以我们从根节点开始修改。

每次在左右儿子中找出包括 x x x 的节点来修改。

然后继续往下递归,直到没有左右儿子。

时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)

如果不用线段树维护,单点修改完全可以非常简单做到 O ( 1 ) O(1) O(1)是不是觉得线段树是垃圾呢

区间查询

线段树显然不是垃圾,不然发明他干啥。闲着没事干?

线段树的作用就体现在这了。

如果不用线段树,区间查询我们需要挨个将每个点的值访问一遍才能求和。

但线段树已经将部分区间的和帮我们求好了。

我们只需要把询问区间 [ s , t ] [s, t] [s,t] 分解成线段树求过的若干区间,再求和即可。

对于当前遍历到的节点 u u u,我们要返回 [ l u , r u ] [l_u, r_u] [lu,ru] [ s , t ] [s, t] [s,t] 的和。

如果 [ l u , r u ] [l_u, r_u] [lu,ru] 被完全包括在 [ s , t ] [s, t] [s,t] 中, [ l u , r u ] [l_u, r_u] [lu,ru] [ s , t ] [s, t] [s,t] 的和就是 [ l u , r u ] [l_u, r_u] [lu,ru] 的和,直接 return 即可。

如果 u u u 儿子的区间(即 [ l v , r v ] [l_v, r_v] [lv,rv])与 [ s , t ] [s, t] [s,t] 有交集,则遍历 u u u 所有有交集的儿子,返回儿子答案的和。

时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)

比不用线段树快了吧。

区间修改单点查询

有一个长度为 n n n 的数组 a a a,有 m m m 个操作

  1. 询问 a x a_x ax 的值
  2. a a a 在区间 [ s , t ] [s, t] [s,t] 内的所有值加 v v v

1 ≤ n , m ≤ 1 0 6 1\leq n, m \leq 10^6 1n,m106

区间修改

区间修改显然无法将与修改区间有交集的区间值全部更改,怎么办?

需要用到差分思想。

如果我们在单点查询的时候不查询单个点,而是改成前缀和,查询 [ 1 , x ] [1, x] [1,x] 的和来作为答案。

我们区间修改就可以只修改两个点 s s s t + 1 t+1 t+1 了。

我们把 b s b_s bs v v v b t + 1 b_{t + 1} bt+1 v v v

因为如果查询前修改了 [ s , t ] [s, t] [s,t] 的值,查询时 x x x [ s , t ] [s, t] [s,t] 内,则 [ 1 , x ] [1, x] [1,x] 会包括 s s s 但不包括 t + 1 t+ 1 t+1,值就比修改前的正好增加了 v v v

如果查询时 x x x [ t + 1 , n ] [t+1,n] [t+1,n] 内,则 [ 1 , x ] [1,x] [1,x] 会包括 s s s t + 1 t+1 t+1,相互抵消,值依旧不变。

单点查询

通过刚才分析,单点查询改成查询 [ 1 , n ] [1,n] [1,n] 即可。

区间修改区间查询

有一个长度为 n n n 的数组 a a a,有 m m m 个操作

  1. 询问 a a a [ s , t ] [s, t] [s,t] 的和
  2. a a a 在区间 [ s , t ] [s, t] [s,t] 内的所有值加 w w w

1 ≤ n , m ≤ 1 0 6 1\leq n, m \leq 10^6 1n,m106

这下好了,不能使用差分思想了。因为询问也变成区间的了。

怎么办?

区间修改

我们需要一个方法,使得能在 O ( log ⁡ n ) O(\log n) O(logn) 的时间内完成区间修改操作。

我们尝试使用类似于区间查询时的做法。

对于当前遍历到的节点 u u u,我们要修改 u u u 储存的值使得其符合修改 [ s , t ] [s, t] [s,t] u u u 所代表的区间 [ l u , r u ] [l_u, r_u] [lu,ru] 内值的和。

如果 [ l u , r u ] [l_u, r_u] [lu,ru] 被完全包括在 [ s , t ] [s, t] [s,t] 中,则表示 [ l u , r u ] [l_u, r_u] [lu,ru] 内所有值都需要增加 w w w

我们又知道区间 [ l u , r u ] [l_u, r_u] [lu,ru] 的长度,所以我们能够直接算出节点 u u u 修改后要增加的值,即 ( r u − l u + 1 ) × v (r_u-l_u+1)\times v (rulu+1)×v

u u u 子树怎么办?我们现在没时间处理,于是打上一个 l a z y t a g e lazytage lazytage 懒标记,lazy[u] += v,让之后再来处理。

什么时候处理?遇到就处理。

当区间 [ l u , r u ] [l_u, r_u] [lu,ru] 没有被完全包括在 [ s , t ] [s, t] [s,t] 中时 ,就意味着我们要访问儿子节点。

这时候,就顺带处理 l a z y u lazy_u lazyu,也就是懒标记下传,我们将两个儿子的 l a z y lazy lazy 都加上 l a z y u lazy_u lazyu,两个儿子的值都增加其所代表区间长度乘上 l a z y u lazy_u lazyu。最后,不要忘了懒标记清零,lazy[u] = 0

懒标记下传后,我们遍历代表区间与 [ s , t ] [s, t] [s,t] 有交集的儿子。

然后用儿子的信息更改 u u u 的信息。

时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)

区间查询

同理,区间查询与原来单点修改时一样。

只不过增加了懒标记下传过程而已。

线段树优化

标记永久化

线段树使用中的一个技巧,即不下传懒标记。

如何实现?我们在区间查询的时候,路过标记时将标记的影响添加到答案上。

线段树扩展

动态开点线段树

如果线段树空间复杂度太高,且初始每个节点数据基本统一,则可以使用动态开点线段树。

动态开点线段树就不能使用 o ∗ 2 o*2 o2 的方法来表示儿子节点了。

需要另开数组或者用结构体储存左右儿子的下标。

当要访问一个节点 u u u 的左或右儿子时,若 u u u 要访问的儿子还未创建,则根据初始节点数据来创建一个节点 v v v。并将 u u u 的儿子标记为 v v v

即需要节点时才创建节点。

可持久化线段树

一个记录历史版本的线段树。

具体是什么样呢?非常简单。

直接记录历史版本非常容易爆空间,所以我们只需要想一个办法来减少空间使用即可。

我们发现因为每次只修改部分值,所以历史版本有很多重复节点。

我们只需要不新开重复节点就行了。每次只增加修改了的节点。

具体来说是这样的(借了一下这篇文章里的图,我不想自己画了 ):

在这里插入图片描述
相信大家一看就懂。

李超线段树

李超线段树一般用于解决坐标系中线段相关问题,有时可以转化成其他形态。

有一个平面直角坐标系, m m m 个操作。

  1. 添加一个左右端点为 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1), (x_2,y_2) (x1,y1),(x2,y2) 的线段。( x 1 ≠ y 1 x_1\not=y_1 x1=y1
  2. 询问与直线 x = k x=k x=k 相交线段的交点中纵坐标最大的交点的纵坐标。

1 ≤ n , m ≤ 1 0 6 1\leq n,m \leq 10^6 1n,m106

我们可以把第一个操作看成区间修改,第二个操作看成单点查询

于是问题就好办了。

接下来讲一下两个操作的步骤:

区间修改

对于每个线段我们可以看成一个定义域为一段区间的一次函数。

现在要新增一个定义域为一段区间的一次函数 f f f

考虑这样的方法:我们每个节点不储存信息,只打懒标记(懒标记为一个函数),并使用标记永久化技巧。

询问时,则取节点区间包含了 x = k x = k x=k 的节点的懒标记中在 x = n x = n x=n 上取值最大懒标记。

假设我们现在递归到了节点 u u u,其懒标记为 g g g、对应区间为 [ l , r ] [l, r] [l,r],而且 [ l , r ] [l, r] [l,r] f f f 定义域覆盖。

如果在 x = ⌊ l + r 2 ⌋ x = \left\lfloor \frac{l+r}{2}\right\rfloor x=2l+r f f f g g g 取值大,则将 f f f g g g 交换。

接下来只讨论另一种情况。

节点 u u u 的懒标记已经不需要更新了,但我们还需要判断其左右儿子懒标记是否需要更新,因为可能在左右儿子的区间中点处 f f f 取值比 g g g 取值大。

所以我们可以分类讨论 f f f g g g 斜率正负号、或者直接解出 f f f g g g 的交点(也可以用其他方法)来判断左右儿子懒标记是否需要更新。

查询交点

查询的时候,我们在所有 节点区间 包含 k k k 的节点的懒标记中寻找出在 x = k x = k x=k 取值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手写RPC框架02-路由模块设计与实现

源码地址:https://github.com/lhj502819/IRpc/tree/v3 系列文章: 注册中心模块实现路由模块实现序列化模块实现过滤器模块实现 为什么需要路由模块? 在当今互联网日益发展的情况下,我们一个服务一般都会部署多个,一方…

Python绘制表白代码,又是一个表白神器

前言 嗨呀,又是我,又给你们带来了表白的代码 之前发了那些 照片里面加文字的…还有烟花…还有跳动爱心…emm你们也可以去看看哦 今天带来的这个,也是很不错哦 只不过它出来的有些慢,我这里先给你们看看这个效果图吧 效果展示…

大数据基础平台搭建-(三)Hadoop集群HA+Zookeeper搭建

大数据基础平台搭建-(三)Hadoop集群HAZookeeper搭建 大数据平台系列文章: 1、大数据基础平台搭建-(一)基础环境准备 2、大数据基础平台搭建-(二)Hadoop集群搭建 3、大数据基础平台搭建-&#xf…

如何让小型云台机械手实现按颜色分拣物品?

1. 功能说明 在小型云台机械手附近设置一个工作台,并安装一个TCS3200颜色识别传感器。将红色、蓝色工件分别放置在传感器上,如果检测的物料的颜色为红色,机械臂将物体放在机械臂的左侧,如果检测的物料的颜色为蓝色,机械…

数据结构与算法-希尔排序、归并排序

目录​​​​​​​ 希尔排序 1.算法描述 2.算法的实现 归并排序 4.1算法描述 2.算法实现 希尔排序 1.算法描述 1959年shell发明,第一批突破O(n2)时间复杂度的排序算法,是简单插入排序的改进版。它与插入之处在于&#xff0…

Android 深入系统完全讲解(二)

操作系统 操作系统是一套软件,它的任务就是为上层开发的用户,提供一个更方便的开发环境,同时 让硬件连接到系统中,能够非常方便,从而提高开发速度,以及稳定可靠。 操作系统就是这么存在的。 我们理解它&am…

基于低代码平台构筑金融行业IT运维服务体系

我今天分享题目是《基于低代码平台,构筑金融行业的IT运维服务体系》。这是一个大家不太熟悉的领域,首先它的行业是金融,其次它做的事情是IT运维。 关于金科信息 我先介绍一下金科信息。金科信息在1993年成立,到明年我们就整整30…

前端开发技术栈(插件篇):datatables

一、Datatables介绍 官网地址:https://datatables.net/ DataTables中文网:http://datatables.club/ 1、Datatables是一款jquery表格插件。它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能。 2、分页,即时搜索和排序…

【Bootstrap】可复用的组件

目录 一、字体图标 二、下拉菜单 1. 步骤 2. 对齐 3. 分割线 4. 禁用的菜单项 三、按钮组 1. 按钮组 2. 按钮工具栏 3. 尺寸 4. 嵌套 5. 垂直排列 四、输入框组 1. 输入框组 2. 尺寸 3. 作为额外元素的按钮 4. 作为额外元素的按钮式下拉菜单 一、字体图标 组件…

55、MySOL数据库

目录 一、MySQL安装和配置 二、数据库 三、表 四、数据库的C [create] R [read] U [update] D [delete] 语句 1、insert语句 2、update语句 3、delete语句 4、select语句 五、Mysql常用数据类型(列类型):​编辑 六、函数 *合计 / 统计…

kettle - 清洗 mongodb 数据案例

文章目录前言kettle - 清洗 mongodb 数据案例一、需求二、kettle开发1、新建mongodb数据查询2、配置kettleTest集合与清洗后kettleTestClear集合字段映射3、根据_id进行排序4、使用java脚本将日期格式化5、进行字段选择6、将delete字段进行值映射7、mongo输出8、最后加一个写日…

详解哨兵之间是如何通信的

基于 pub/sub 机制的哨兵集群组成 哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。 哨兵只要和主库建立起了连接,就可以在主库上发布消息了,比如说发布它自己的连接信息(IP 和…

PDF如何转换成PPT?教你们几个简单方法

我们在工作经常用PDF文件进行传输,因为PDF体积小,传输速度很快,还不会不同设备上出现不兼容的问题,可以很好的保持文件的排版,不过我们有时候需要进行文件的展示,所以需要将PDF文件转换为PPT文件&#xff0…

2023河南/北京/重庆/南京DAMA-CDGA/CDGP数据治理工程师认证报名

DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业…

论文阅读——Recognizing Emotion Cause in Conversations

文章目录摘要引言相关工作任务定义构造RECCON数据集情绪原因的类型实验任务1:Causal Span Extraction模型任务2:Causal Emotion Entailment模型面临的挑战摘要 识别文本中情绪背后的原因是NLP中一个未被探索的研究领域。这个领域的发展具有着改善情感模…

Docker基础1-3

Docker基础1-3 时间:2023-01-02 https://www.bilibili.com/video/BV1gr4y1U7CY/ xmind文档:https://www.aliyundrive.com/s/6iaQt9zLDVm 一、Docker简介 1、Docker解决了什么问题 Docker打破了过去「程序即应用」的观念。透过镜像(images)将作业系统核心除外&am…

LeetCode:15. 三数之和

15. 三数之和1)题目2)思路3)代码4)结果1)题目 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] …

【Nginx】核心配置文件结构

文章目录Nginx核心配置文件结构全局块user指令work process指令其他指令events块events指令配置实例http块定义MIME-Type自定义服务日志其他配置指令server块和location块Nginx服务器基础配置实例Nginx核心配置文件结构 从前面的内容学习中,我们知道Nginx的核心配置…

给力!低代码开发平台广州流辰信息科技助您增辉创价值!

低代码平台开发公司流辰信息深耕行业多年,一直以市场为导向,凭借敏锐的市场洞察力砥砺前行、拼搏进取,提升研发创新能力,广州流辰信息科技与各新老客户朋友风雨同舟,携手共创宏伟新蓝图! 一、熔铸前沿科技 …

kettle的安装以及注意(迭代中....)

1、下载 kettle的官网下载地址:Pentaho from Hitachi Vantara - Browse Files at SourceForge.net 如果需要下载其他版本: 直接点击对应的版本Name(8.0以下的实在Data Integration文件夹里面)进去,再选择client-too…