线段树专题(1)

线段树

线段树可维护的信息类型

        线段树可以维护的信息类型,父范围上的某个信息,可以用O(1)的时间,从子范围的信息加工得到,例如求某个范围的最大最小值,给某个范围每个位置加相同的数字,进行求和。

0到2范围上的最大值是5,3到5范围上的最大值是7,如果求0到5范围的最大值,就是5和7进行比较,这样子就很快。 已知2到4范围上的和是15,那么给这三个位置的值都增加2,那么和就是15+2*3=21,也很快;
        那么还有一些不能够被维护的信息类型,例如:某个范围出现次数最多的数字

 对于0到4范围上,出现最多的数字是3,5到9范围上,出现最多的数字是1,但是在0到9范围上出现的最多的数字不是1和3,而是5,父范围上的信息,不能够O(1)的时间由子范围得到,那么这样子的就是不能被线段树维护的信息。

线段树的经典功能:范围查询和范围修改操作单次调用的时间复杂度为O(logN)

线段树的操作

以经典的累加和为例:

  1. 线段树可以1下标开始,也可以0下标开始,1下标开始是经典的设定。
  2. 线段树可以在初始化时,指定范围的规模[1,n],一旦确定不能改变
  3. 任何一个大范围[l,r],严格从中点拆分为左[l,mid]和右[mid+1,r]
  4. 每个范围的信息,填在独立的,连续的数组中,最大范围的[1,n]填在sum[1]
  5. 如果父信息在sum[i],那么左范围sum[i*2],右范围[i*2+1]
  6. 范围[l,r]和i值对应,由递归去维护,无需记录对应关系

 对于维护节点个数不是2^n次方的范围来说

 由于子范围和父范围有相对应的关系,那么可以不用记忆一个范围对应在sum数组中的什么位置,直接由父信息在sum[i],那么左范围sum[i*2],右范围[i*2+1]进行维护,那么实际上应该开多大的数组?2*n+1?不可以,用实际的例子展示下。

数组多大空间

如图:这样子超出了2*n+1的范围

 那么应该如何去做,求得实际的空间。应该是根据树高去求得节点的个数如果节点个数是2^n次方,那么展开就是一颗完全二叉树,树的高度(log以2为底n的对数+1,)对于不是2^n节点的,需要的节点个数是大于当前节点且最接近的节点个数,那么树高就是log以2为底n的对数+2,因此可以得出需要开辟的空间为4*n,也可以通过代码进行演示

// [l,r]在数组的i位置
// 返回递归过程展开的最大编号
int maxi(int l, int r, int i) {if (l == r) {// 递归到了1个节点,直接填return i;}else {// 不止一个int mid = (l + r) >> 1;// i << 1 | 1  -->  (i * 2 + 1) return max(maxi(l, mid, i << 1), maxi(mid + 1, r, i << 1 | 1));}
}int main() {int n = 10000;	// 测试范围int a = 0;		// 最大倍数的iint b = 0;		// 最大倍数的需要的空间double t = 0;	// 最大倍数for (int i = 1; i <= n; i++) {int space = maxi(1, i, 1);	// 获取最大编号double times = space / (double)i;	// 倍数cout << "范围[1~" << i << "]," << "需要空间" << space << ",倍数=" << times << endl;if (times > t) {a = i;b = space;t = times;}}cout << "其中的最大倍数,范围[1~" << a << "]," << "需要空间" << b << ",倍数=" << t << endl;return 0;
}

建树

void up(int i) {// 父范围的累加和 = 左范围累加和 + 右范围累加和sum[i] = sum[i << 1] + sum[i << 1 | 1];
}// 建树
void build(int l, int r, int i) {if (l == r) {// 只有一个,直接填sum[i] = arr[l];}else {int mid = (l + r) >> 1;// 左右调用求和build(l, mid, i << 1);build(mid + 1, r, i << 1 | 1);up(i);}
}

查询

query(jobl,jobr,l,r,i):jobl和jobr代表需要查询任务的范围,l和r代表当前在的范围,i代表当前是sum数组的哪一个位置。

 例如:求得[2,7]范围上的和。query(2,7,1,8,1),从顶部开始向下寻找,[1,4]位置包含,拆分为[1,2]和[3,4],对于[1,2]只有右边的2在[2,7]中,向上返回sum中9位置的数字,对于[3,4]都包含在[2,7]中,向上返回sum中5位置的数字,数字相加返回到顶部;同理,[5,8]范围包含,得到sum中6位置的数字和sum中14位置的数字,数字相加返回到顶部。
        从这个过程可以看出,某些位置可以不遍历到树的根节点,因此大大节省了时间。时间复杂度为logn,对于根节点的两边展开,大致是沿着一条线下去的,部分返回的过程中不需要完全展开到根节点。

// 任务范围[jobl,jobr]
// 线段树维护范围[l,r]
// 在sum中的位置i,在[l,r]的和是i
long query(int jobl, int jobr, int l, int r, int i) {if (jobl <= l && r <= jobr) {// 范围全命中,不用向下展开了// 需要[2,5],现在是[3,4]return sum[i];}// 没有全命中,任务下发 int mid = (l + r) >> 1;// 信息下发 -- 后面的方法讲了信息下发的过程// 如果没有down,可能更新不对,例如查询[2,3]范围的和// 假设上次的懒更新到了[1,2]和[3,4],没有往下更新,那么进行查询时// 获取到的2和3位置的值是错误的,即add数组相关位置是错误的down(i, mid - l + 1, r - mid);long ans = 0;if (jobl <= mid) {// 任务命中,左侧还需要调用ans += query(jobl, jobr, l, mid, i << 1);}if (jobr > mid) {// 任务命中,右侧还需要调用ans += query(jobl, jobr, mid + 1, r, i << 1 | 1);}return ans;
}

懒更新

void add(jobl,jobr,jobv,l,r,i):在[jobl,jobr]范围上,每个数组增加jobv;来到[l,r]范围上,信息存储的位置是i.

调用add(jobl,jobr,jobv,1,n,1),范围增加的递归,懒更新机制

  • 任务[jobl,jobr]把当前范围[l,r]全覆盖,不在向下传递任务
    add[i] += jobv;sum[i] += jobv * (r - l  + 1)
  • 如果任务不能把当前范围全包,把当前范围积攒的懒信息只往下发一层,发过了不必要重复发送,然后决定往左有范围,进行递归,递归完成后,利用左右sum信息,把当前范围的sum[i]信息修改正确
  • 退出递归

当原数组长的是8,每个位置是0,可以构建出15个节点的线段树,现在在某个范围上增加相同的值。需要add数组和sum数组,长度为线段树节点的个数(15),add数组存储某位置或者某段位置增加的数值,sum数组存储进行更新后得到的增加的和。
初始状态:

 进行[1,8]位置增加5:由于根节点位置包含在[1,8]中,因此只更新数组的1位置,得到:

[5,6]位置增加3:由于[5,6]没有把[1,8]覆盖掉,因此要进行懒更新,将积攒的信息发下去:add[2] = 5,add[3] = 5,add[1] = 0,由于add[2]和add[3]进行更新,那么sum数组对应的sum[2] = 20,sum[]3] = 20,总和不变,因此sum[1]不变,还是40 。[1,4]中没有[5,6],不用向下,[5,6]在[5,8]但是没有被完全覆盖,因此进行懒更新下发:add[6] = 5,add[7] = 5,同理sum数组进行更新sum[6] = 10,sum[7] = 10.

 此时[5,6]范围包住了需要增加的[5,6],直接进行更新,不用下发了,进行更新,add[6] = 5+3;对应的sum[6] = 10+2*3;那么sum[3] = 20+2*3;sum[1] = 40+2*3得到:

[2,6]位置增加-2:没有被[1,8]完全包住,由于上次已经进行了懒更新下发,因此直接到[1,4]和[5,8]位置,没有被[1,4]完全包住,进行懒更新下发,没有被[5,8]完全包住,由于已经下发,直接到下一层。
 

 同理[1,2]进行更新

左侧无懒更新下发,进行数值更新,add[9] = 5 - 2,add[5] = 5- 2 ,相对应的sum进行更新得到如下表格 :

 右侧更新,add[6] = 8 - 2 ,sum更新。任务完成

 

// 来到[l,r]对应的下标是i,范围上数字个数n == r - l + 1
void lazy(int i, long v, int n) {// sum和add进行调整sum[i] += v * n;add[i] += v;
}void up(int i) {// 父范围的累加和 = 左范围累加和 + 右范围累加和sum[i] = sum[i << 1] + sum[i << 1 | 1];
}// 懒信息的下发
// [l,r] i 懒信息
// [l,mid] i * 2   ln表示左侧几个数
// [mid+1,r] i*2+1 rn表示右侧几个数
void down(int i, int ln, int rn) {// 留着信息才下发if (add[i] != 0) {// 发左lazy(i << 1, add[i], ln);// 发右lazy(i << 1 | 1, add[i], rn);// 父范围懒信息清空add[i] = 0;}
}// jobl ~ jobr范围上每个数字增加jobv
void add(int jobl, int jobr, long jobv, int l, int r, int i) {// 当前任务把整个范围全包了,不用往下发了if (jobl <= l && r <= jobr) {lazy(i, jobv, r - l + 1);}else {// 没有全包int mid = (l + r) >> 1;// 懒更新下发,下发左和下发右down(i, mid - l + 1, r - mid);if (jobl <= mid) {// 左边add(jobl, jobr, jobv, l, mid, i << 1);}if (jobr > mid) {// 右边add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1);}// 信息汇总up(i);}
}

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

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

相关文章

Linux应用开发基础知识——tslib应用编程(十一)

文章目录 一、tslib是啥&#xff1f;二、tslib 框架分析三、交叉编译、测试 tslib3.1、交叉编译tslib&#xff08;1&#xff09;设置交叉编译工具链&#xff08;2&#xff09;进入tslib目录&#xff08;3&#xff09;安装工具链&#xff08;4&#xff09;确定工具链中头文件、库…

MySQL必会知识精华6(组合WHERE子句)

我们的目标是&#xff1a;按照这一套资料学习下来&#xff0c;大家可以完成数据库增删改查的实际操作。同时轻松应对面试或者笔试题中MySQL相关题目。 上篇文章我们先做一下数据库的where条件过滤的方法&#xff0c;都是单个条件的过滤。本篇文章主要介绍查询的组合WHERE子句的…

系统架构师2023版:习题

架构设计基础 计算机基础 目前处理器市场中存在 CPU 和 DSP 两种类型的处理器&#xff0c;分别用于不同的场景&#xff0c;这两种处理器具有不同的体系结构&#xff0c;DSP采用()。 A.冯诺依曼结构 B.哈佛结构 C.FPGA 结构 D.与 GPU 相同的结构 解析:…

企微SCRM价格解析及其性价比分析

内容概要 在如今的数字化时代&#xff0c;企业对于客户关系管理的需求日益增长&#xff0c;而企微SCRM&#xff08;Social Customer Relationship Management&#xff09;作为一款新兴的客户管理工具&#xff0c;正好满足了这一需求。本文旨在为大家深入解析企微SCRM的价格体系…

RocketMQ学习笔记

RocketMQ笔记 文章目录 一、引言⼆、RocketMQ介绍RocketMQ的由来 三、RocketMQ的基本概念1 技术架构2 部署架构 四、快速开始1.下载RocketMQ2.安装RocketMQ3.启动NameServer4.启动Broker5.使⽤发送和接收消息验证MQ6.关闭服务器 五、搭建RocketMQ集群1.RocketMQ集群模式2.搭建主…

基于AI大模型开发应用层产品经典解决方案:ASR+LLM+TTS

在 AI 大模型开发领域&#xff0c;ASR&#xff08;自动语音识别&#xff09;LLM&#xff08;大语言模型&#xff09;TTS&#xff08;语音合成&#xff09;的解决方案是一种将语音输入、语言理解和语音输出整合在一起的技术架构&#xff0c;能够实现智能的语音交互应用。 方案介…

tree-transfer-vue3插件(树形数据穿梭框)

tree-transfer-vue3 效果图 简介 tree-transfer-vue3 是一个基于 VUE 和 element-plus 的树形穿梭框组件&#xff0c;使用前请确认已经引入element-plus&#xff01; 此组件功能类似于element-plus的transfer组件&#xff0c;但是里面的数据是树形结构&#xff01; 实际上&am…

临床检验方法与仪器 第一部分作业:光谱分析仪器与技术的总结与归纳+新型光谱仪的调研

临床检验方法与仪器 第一部分作业 列表归纳紫外-可见分光光度计、荧光光谱分析仪、原子吸收光谱仪、原子发射光谱仪的原理、特点、技术优势和主要应用对象&#xff1b;调研新型光谱仪&#xff0c;每一类至少提供1个例子&#xff0c;列出图片、厂家、型号、主要技术特点和优势。…

Linux系统编程-多线程线程属性

如何查看有那些多线程系统调用属性api 线程属性系统api举例 /* int pthead_attr_init(pthread_attr_t *attr); -对属性变量初始化int pthread_attr_destroy(pthread_attr_t *attr); -使用完毕需要销毁int pthread_attr_getdetachstate(const pthread_attr_t *attr, int*detach…

LVGL加入外围字库

一、首先lvgl是有自带字库的 lvgl/src/font 如下图 二、但如果这个字库不能满足我们的需求我们就要外建字库。 1、字库生成软件LVGL官网,字体转换器 — LVGL如下图: 最后按“提交”就可以看到有一个字体被下载到你电脑里。他是以.c文件的型式,把它COPY到lvgl的根目录下 2、…

【Steam登录】protobuf协议逆向

https://api.steampowered.com/IAuthenticationService/GetPasswordRSAPublicKey/v1 搜索 input_protobuf_encoded定位 input_protobuf_encoded的值就是 o s r.SerializeBody() o i.iI(s) 精准定位 打上条件断点&#xff1a;t ‘Authentication.GetPasswordRSAPublicKey…

ML 系列:第 21 节 — 离散概率分布(二项分布)

一、说明 二项分布描述了在固定数量的独立伯努利试验中一定数量的成功的概率&#xff0c;其中每个试验只有两种可能的结果&#xff08;通常标记为成功和失败&#xff09;。 二、探讨伯努利模型 例如&#xff0c;假设您正在抛一枚公平的硬币 &#xff08;其中正面成功&#xff…

【模拟集成电路】知识点笔记_1

知识点笔记_1 零极点相关1 PM和GM相关概念2零极点 温度系数五种常见噪声源MOS管和BJT选取BJT刨面图工艺角衬底主要噪声来源共模反馈三种常用CMFB1 工作在线性区MOS作为CMFB&#xff08;匹配决定输出电压&#xff09;2 电阻反馈&#xff08;Buf&#xff09;3 电流差分对&#xf…

资产管理:SpringBoot框架的高效解决方案

6系统测试 6.1概念和意义 测试的定义&#xff1a;程序测试是为了发现错误而执行程序的过程。测试(Testing)的任务与目的可以描述为&#xff1a; 目的&#xff1a;发现程序的错误&#xff1b; 任务&#xff1a;通过在计算机上执行程序&#xff0c;暴露程序中潜在的错误。 另一个…

Redis - 集群(Cluster)

一、基本概念 上述的哨兵模式,提⾼了系统的可⽤性.但是真正⽤来存储数据的还是master和slave节点.所有的数 据都需要存储在单个master和slave节点中. 如果数据量很⼤,接近超出了master/slave所在机器的物理内存,就可能出现严重问题了. 如何获取更⼤的空间?加机器即可!所谓&q…

基于springboot的高校科研管理系统(源码+调试+LW)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据你想解决的问题&#xff0c;今天给…

pwn学习笔记(11)--off_by_one

pwn学习笔记&#xff08;11&#xff09;–off_by_one ​ 在处理for循环或者while循环的时候&#xff0c;有的可能会遇到如下情况&#xff1a; #include<stdio.h>int main(){char buf[0x10];for (int i 0 ; i < 0x10 ; i ){buf[i] getchar();}puts(buf);}​ 多次输…

YOLOv8模型改进 第十九讲 添加倒置残差移动块iRMB(Inverted Residual Mobile Block,) 去除图像噪声

本文这次分享的是倒置残差移动块iRMB&#xff0c;iRMB&#xff08;Inverted Residual Mobile Block&#xff09;的作用主要是在神经网络中实现高效的特征提取&#xff0c;它融合了卷积神经网络&#xff08;CNN&#xff09;捕捉局部特征的高效性和 Transformer 动态建模长距离交…

express项目中使用MySQL

一、安装mysql 模块 1.1 先配置包管理工具 npm init -y1.2、安装mysql 模块 npm install mysql2二、配置mysql // 1、导入mysql模块 const mysql require("mysql2");// 2、建立与mysql 数据库的链接 const db mysql.createPool({host: "127.0.0.1", …

泛微E9 OA与金蝶云的差旅费报销接口集成

FD001-差旅费报销申请 泛微>金蝶--498 集成案例分享 在企业日常运营中&#xff0c;差旅费报销申请的处理效率直接影响到员工满意度和财务管理的精确性。为了实现泛微OA-Http系统与金蝶云星空平台之间的数据无缝对接&#xff0c;我们设计并实施了FD001-差旅费报销申请集成方…