超级用心的线段树介绍

C++ 线段树 线段树基本常识 线段树是一种二叉搜索树(即每个节点最多有两颗子树),与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 如下图,就是一颗 [1,10] 的线段树的分解过程: 由图可得出一个结论,线段树的最大深度不超过,且每一颗线段树需要 4*n 的辅助空间,如果没有开到 4*n ,那就完美 RE 了!!! 线段树的的存储 线段树采用的是堆式储存法,即编号为 k 的节点的做儿子为 k... 阅读详情

洛谷同步。

前:笔者觉得线段树很重要,听说我基友___X3___要写一篇关于线段树的博客,我就也写了一篇介绍详细易懂的博客。持续更新。

-----------------------咕咕的分界线------------------------------------------------

更新日志:

update 11.21/2020 :写成。

-----------------------咕咕的分界线------------------------------------------------

Part 1

线段树是一种基于分治思想的二叉树结构,较通用,适于解决区间问题。

线段树的每个节点代表一个区间,根节点唯一,表示 1 − n 1-n 1−n。

每个叶节点表示一个长度为1的元区间 [ x , x ] [x,x] [x,x]。

对于每个节点(内部) [ l , r ] [l,r] [l,r],它的左节点是 [ l , m i d ] [l,mid] [l,mid],右子节点是 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]。

m i d = ( l + r ) / 2 = ( l + r ) > > 1 mid=(l+r)/2=(l+r)>>1 mid=(l+r)/2=(l+r)>>1

( m i d mid mid 向下取整)

-----------------------咕咕的分界线------------------------------------------------

性质:线段树除去最后一层,一定是一棵完全二叉树,所以,在理想情况下, n n n 个节点的线段树有
n + n / 2 + n / 4 + . . . + 2 + 1 = 2 n − 1 n+n/2+n/4+...+2+1=2n-1 n+n/2+n/4+...+2+1=2n−1
个节点。

因此,数组长度要开到不小于4倍。

我暴力开过100倍,不会MLE…

-----------------------咕咕的分界线------------------------------------------------

图片链接:(图老是挂,于是决定使用链接。)

注意,线段树的编号与堆的编号类似,相当于 d f s dfs dfs 序。

线段树图片

------------所以,第一部分圆满结束---------------------------------

Part 2,标记延迟

这就是大名鼎鼎臭名远扬的 l a z y t a g lazytag lazytag。

先说一下标记延迟的作用:将一次区间修改的时间复杂度从 O ( n ) O(n) O(n) 优化到 O ( l o g n ) O(log n) O(logn)。

本文 l o g log log 默认为2。

如果在修改指令中,要修改节点 p p p 代表的区间 [ l , r ] [l,r] [l,r]。正常的修改方法就是访问以它为根节点的子树并修改。复杂度是 O ( n ) O(n) O(n),其中 n n n 为此子树节点数。

但是,我们没有用到这个区间作为候选答案。所以,更新 p p p 的整棵子树是浪费时间的徒劳行为。

怎么解决呢???

加入 l a z y t a g lazytag lazytag,懒标记。

ps:关于标记,还有一种叫做“标记永久化”的技巧。就是不下传标记,而是累计递归时产生的标记影响。这种做法局限性大,仅在二维线段树和可持久化线段树中有所应用。所以不介绍了。

懒标记的具体实现就是在回溯时向节点 p p p 增加一个标记,表示“该节点在过去被修改了,但是它的子节点尚未被更新”。

后续操作中,如果要从 p p p 往下递归,我们就再检查一下 p p p 是否有标记。有,就根据标记更新 p p p 的两个儿子,并往下增加标记,然后消除 p p p 的标记。

这种优化叫做延迟标记,是一个高效率的线段树应该具有的优化。

Part 3 ,Code

上代码。

线段树初始化:

struct segtree{
	int l,r,lazy,add;
}t[max_size<<1+max_size<<1];

有空更新一份用 c l a s s class class 写的。

从子节点往父亲更新标记:

void pushup(int id)
{
	t[id].add=t[id<<1].add+t[id<<1|1].add;
} 

下传标记:

void pushdown(int id,int l,int r){
	t[id<<1].lazy+=t[id].lazy;
	t[id<<1|1].lazy+=t[id].lazy;
	int mid=(l+r)>>1;
	t[id<<1].add+=t[id].lazy*(mid-l+1);
	t[id<<1|1].add+=t[id].lazy*(r-mid);
	t[id].lazy=0;	
}

线段树递归建立:

void build_segtree(int id,int l,int r){
	//节点id表示区间 [l,r]
	t[p].l=l,t[p].r=r;
	if(l==r){
		//叶节点
		t[p].add=a[l];
		return; 
	}
	int mid=(l+r)>>1;
	build_segtree(id<<1,l,mid);
	build_segtree(id<<1|1,mid+1,r);
	//等同于build_segtree(id*2+1,mid+1,r); 
	pushup(id);
	//标记延迟优化时间 
}

上传操作和下传操作是维护线段树的基本操作,基本上每一种操作都要用到。

还有,每个人的线段树写的都不一样,我的写法适中。所以,关于线段树,尽量不要背模板,要随机应变。

有一种比较经典的 s p r e a d spread spread 函数,其实就是 p u s h d o w n pushdown pushdown 函数。

单点修改和区间修改中很重要的一点:

判断是否向下递归,需判断覆盖性,如果当前区间 [ l , r ] [l,r] [l,r] 被完全覆盖,就可以直接返回了。就是一种整块的处理,有没有分块的感觉?

单点修改,将区间 [ x , y ] [x,y] [x,y] 的每一个值加上 v v v:

int add_(int id,int l,int r,int x,int y,int v){
	if(x<=l&&r<=y){
		t[id].lazy+=v;
		t[id].add+=v*(r-l+1);
		return;//叶节点修改 
	}
	pushdown(id,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)add_(id<<1,l,mid,x,y);
	if(y>mid)add_(id<<1|1,mid+1,r,x,y);
	pushup(id);
}

区间修改,求出区间 [ x , y ] [x,y] [x,y] 的每一个值的和:

int query(int id,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		return t[id].add;
	}
	pushdown(id,l,r);
	int ans=0,mid=(l+r)>>1;
	if(x<=mid)ans+=query(id<<1,l,mid,x,y);
	if(y>mid)ans+=query(id<<1|1,mid+1,r,x,y);
	return ans;
}

有了这些操作,就可以实现大部分的线段树题目了。

-----------------------咕咕的分界线------------------------------------------------

Part 4 例题及题解

1.最经典的线段树1,只要把上面代码组合一下就可以过了。此处不贴。

2.守墓人,把上面的代码组合修改就可以过了。我是用分块AC的,代码传送门。

3.题目是一道简单的省选,最大数,代码传送门我的博客(题解)。

4.线段树2,这道题难就难在区间乘。为了时间和精度,我们使用两个 l a z y t a g lazytag lazytag,乘法优先更新,就可以跑过了。

证明:乘法优先的话,不会改变加法的 l a z y t a g lazytag lazytag
的值,使得精度等一系列更优。证毕。

5.最难的一题:线段树3,由于有历史情况,所以我们要维护4种标记,然后跑过。代码和 灵梦 的非常像,但没有抄袭,我是对着TA的打了一遍,那时,我刚学线段树。代码:传送门。

-----------------------咕咕的分界线------------------------------------------------

结:

我希望这篇文章对读者有所帮助。若有建议,可直接在评论区提出。转载私聊。相对来说,这篇文章很详细,因为我自己学这个的时候,别的文章大都有些欠缺,就像那位基友的,导致我不好理解。最近有空,就写篇真正的好文章详解吧!!!

如果觉得这篇文章对您有帮助,可以点个赞吗???谢谢阅读。

线段树详解(包含加法线段树、乘法线段树及区间根号线段树,简单易懂) 线段树及简单应用。 阅读详情

相关推荐

线段树合并与线段树分裂

线段树合并是指建立一棵新的线段树,这棵线段树的每个节点都是两棵原线段树对应节点合并后的结果。线段树分裂实质上是线段树合并的逆过程。线段树分裂只适用于有序的序列,无序的序列是没有意义的,常用在动态开点的权值线段树。线段树分裂与线段树合并密不可分,一般来说没有只分裂不合并的题。的救灾粮,问所有操作结束后每个节点类型最多的救灾粮是那种。一棵树,点有点权,问每个节点的子树内有多少节点点权比它大。从这棵树的根节点开始递归分裂,当节点不存在时,递归结束。显然,对于两颗满的线段树,合并操作的复杂度是。

conti123的博客 934

线段树算法总结&专题训练1

1.概述 线段树,顾名思义,是一种基于树的数据结构。线段树跟分块一样,也是用来支持区间操作的(比如区间加,区间求和,区间求 maxmaxmax 等等),而且线段树可以支持很多树状数组做不到的内容;换言之,我们可以将线段树看作升级版的树状数组。而且线段树的可扩展性很强,很多的数据结构都是从线段树上扩展的。 不过线段树也有一些弱点,比如常数比较大,不能支持插入/删除操作,没有办法维护一些东西(但是分块却可以轻易的实现)等等。 所以,虽然线段树可以完成很多事情,但是同样要熟练掌握树状数组与分块。卡常的题目就要用树

Plozia 的小窝 664

二维线段树(线段树套线段树)

由于网上的其它博客对初学者非常不友好,所以我就来写一篇博客帮助萌新们快速入门树套树啦。所谓线段树套线段树就是在第一棵线段树的每一个结点上再开一颗线段树,第一棵线段树用来维护第一维的数据,再用第一棵线段树上新开的线段树来维护第二维的数据。是不是没听懂呢,下面我们直接上图: 这就是树套树大概的模样,横线上面是第一棵线段树,下面是对第一棵线段树的每一个结点新开的线段树,就像是每一个点内都套着一棵线段树一样,那么二维线段树就可以用来维护平面信息下面看一道例题: 陌上花开 首先对第一维进行排序,然后用树套树进行处理

NEUQ_snowy的博客 4434

线段树介绍

文章目录线段树介绍 线段树介绍 先盗个图抛个问题: 图中的RMQ

木风feng的博客 3485

边表法

边表法是用来绘制填充一个面的,边表法的关键是边表和连接边表的扫描线。 对于边的描述利用边表数据结构,包含边与扫描线的交点x坐标,边的最大y坐标,这样可以知道边加入到哪些扫描线上,便还有一个指针来记录扫描线上其它边。 //边数据结构 #pragma once #ifndef __CAETZ__ #define __CAETZ__ #include "pch.h" #include "CPoin...

weixin_42376458的博客 2101

别再纠结了!手把手教你根据项目需求选对目标检测框架(MMDetection/Detectron2/YOLOv8实战对比)

本文详细对比了三大目标检测框架MMDetection、Detectron2和YOLOv8的适用场景与实战技巧,帮助开发者根据项目需求精准选型。从毕业设计到工业落地,分析不同场景下的核心需求与框架优势,提供决策树和避坑指南,确保技术选型与项目目标高度匹配。

weixin_30342209的博客 554

线段树:数据结构中的超级英雄

线段树是一种高效的二叉树数据结构,专门用于处理区间查询和更新操作,如求和、求最大值或最小值等。每个线段树节点代表一个区间,根节点代表整个区间,叶子节点代表单个元素。线段树的特点包括其二叉树结构、区间划分能力以及在O(logn)时间复杂度内完成区间操作的高效性。本文通过C++代码示例详细介绍了如何构建线段树、进行区间查询和单点更新,并提供了一个具体的应用实例,展示了如何在数组上执行这些操作。通过学习线段树,我们可以更有效地解决涉及区间操作的问题,这对于算法学习和实践具有重要意义。

2402_87970811的博客 1268

麒麟系统下PaddleOCR的离线部署实战:从环境搭建到模型优化

本文详细介绍了在国产麒麟操作系统上离线部署百度PaddleOCR的完整实战流程。从系统环境准备、Conda虚拟环境搭建,到PaddlePaddle框架和PaddleOCR的离线安装,再到预训练模型的本地部署与优化技巧,提供了全面的文字识别解决方案。重点解决了离线环境下依赖包管理、模型量化加速等核心问题,并附带了批量处理与内存监控的进阶代码,适用于对数据安全有严格要求的政务、金融等场景。

docker7nomad的博客 963

线段树介绍及线段树的使用场景

假如说你有一个数组,数组下标为 0-1000,然后对外提供一些方法,1.1比如说你对外提供add方法,add方法(1,200,6),请你把从1 到 200 位置所有的值 加上61.2更新(update),传递一个范围,比如 (7,375,4) 7-375范围所有值变为41.3 query 比如(3,999) 查询3到999所有数据的和是多少。如果暴力方法我们怎么来做啊,add方法遍历嘛,遍历1-200 每个加一个6,更新和query也一样。

KudouShinichi3的博客 1060

Ansys Zemax | 公差分析中的补偿器优化:如何提升系统良率?

本文深入探讨了在Ansys Zemax公差分析中如何有效设置与优化补偿器以提升系统良率。文章指出,补偿器是连接理想设计与现实生产的关键,通过模拟实际装配中的调整(如后焦距),能显著改善公差分析的“悲观”预测。详细介绍了补偿器的添加方法、多补偿器策略、评价函数设计以及通过灵敏度分析和蒙特卡洛档案优化补偿效果,旨在帮助光学工程师制定更符合生产实际的公差策略,从而有效提升产品良率。

weixin_28419039的博客 619

【BAAS】基于supabase的知识库详细设计

为满足快速开发需要和减少开发及维护成本,使用BAAS(Backend-as-a-Service)模式可以很好的降低我们的后端维护成本,并提高前端对接效率。一般来说,BAAS是一种云服务模式,BaaS供应商会为提供一些通用的能力,例如用户身份验证、数据库管理、远程更新和推送通知(针对移动应用程序),以及云存储和自托管。BaaS最主流的是Google的firebase,具有相当完善的功能和集成方案,但他是收费的,而且在我们实际项目中,可能需要私有化部署,这时就需要一些开源的替代方案,例如主流的。

Ric的博客 1781

超级详细的线段树入门教程【附模板】

来看一个简单的例子 现在给出一个包含n个整数的数组,对该数组进行m次操作 每次操作给出一个值 op 若 op == 1 则将第 p 的元素改成 k 若 op == 2 则求出区间[l, r]内的最大值并输出 现在来分析这个问题 很显然暴力的做法复杂度高达O(nm),那么现在考虑来优化这个问题 假设给出的数组是 {1, 5, 2, 3, 4, 8} 我们很容易发现 max{1, 5, 2, 3, 4, 8} = max(max{1, 5, 2}, max{3, 4, 8}}; max{1, 5, 2} =

weixin_45761011的博客 1599

【车联网】Automotive 软件过程能力成熟度模型——ASPICE标准入门介绍

ASPICE标准详细定义了与汽车电子软件开发相关的各种术语和概念,包括软件组件、系统集成、测试和验证等。这些定义确保了所有开发团队在使用相同术语时具有一致的理解。

奶爸的编程之路,也就一周冷个三天 2679

【算法】线段树

几乎是算法竞赛最常用的数据结构了,它主要用于(要求满足结合律)。与树状数组相比,它可以实现的,还可以同时(加、乘),更具通用性。如题,已知一个数列,你需要进行下面两种操作:1.将某区间每一个数加上x2.求出某区间每一个数的和第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

weixin_73366312的博客 1808

【LAMMPS学习】八、基础知识(4.5)TIP5P水模型

​ 五点 TIP5P 刚性水模型通过添加两个通常无质量的附加位点 L 来扩展three-point TIP3P model,其中放置与氧原子相关的电荷。这些位点 L 位于距氧原子固定距离处,形成从 HOH 平面旋转 90 度的四面角。因此,这些位点在某种程度上近似于氧的孤对,从而改善了水结构,与four-point TIP4P model相比变得更加“四面体”。

荷塘阅色 1421

OpenCV实例(二)手势识别

手势识别的范围很广泛,在不同场景下,有不同类型的手势需要识别,例如:● 识别手势所表示的数值。● 识别手势在特定游戏中的含义,如“石头、剪刀、布”等。● 识别手势在游戏中表示的动作,如前进、跳跃、后退等。● 识别特定手势的含义,如表示“OK”的手势、表示胜利的手势等。理论基础凸包和凸缺陷在图像处理中具有非常重要的意义,被广泛地用于图像识别等领域。逼近多边形是轮廓的高度近似,但是有时候,我们希望使用一个多边形的凸包来简化它。凸包和逼近多边形很像,只不过凸包是物体最外层的凸多边形。

小幽余生不加糖 9410

线段树简单板子+介绍

这就是经典的二分了,查询这个区间是在哪个结点处,如果这个区间能把当前结点对应的区间包含住,那么我们直接返回这个结点的值就可以了。而第三种情况就是,当你的目标区间和结点区间完全没有交集的时候,我们直接返回0就可以了。“ 这个区间的最大值,取决于 这个区间左半边的最大值,和区间右半边的最大值 ”一个区间的最大值,取决于 这个区间左半边的最大值,和区间右半边的最大值。当结点区间的左端点为目标区间的左端点的时候(结点区间的右端点是绿色处)给你一段区间,然后给你 q次询问,每次询问让你输出这个区间的最大值。

louisdlee的博客 1313

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

【bzoj2006】【NOI2010】【超级钢琴】

2006: [NOI2010]超级钢琴Time Limit: 20 Sec Memory Limit: 552 MB Submit: 1778 Solved: 871 [Submit][Status][Discuss] Description小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。 这架超级钢琴可以弹奏出n个音符,编号为

Vampire 4047

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

线段树详解

1. 什么是线段树 顾名思义 , 线段树是一棵二叉树 , 但不同的是这棵树的结点储存的值是一个数列中 [l,r][l,r][l,r] 的某个需要的值 (例如,求和,求最大值,求最小值) 这是一棵典型的线段树 ,其性质是 : 若其中一子节点编号为 aaa , 则该节点左儿子编号为 2a2a2a , 其右儿子编号为 2a+12a+12a+1 2. 线段树的用处 线段树的用处就是,对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log2n)O(log_2 n)O(log2​n). 3.

orz 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【线段树】线段树入门之入门

线段树的入门级 总结       线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。       对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。       使用线段树可以

生命有一种绝对 4万+

吊打线段树的超级树状数组

你是否讨厌线段树那冗长的代码?你是否还在因为线段树的难调试而满头♂dark汗?那么,请不要错过!超级树状数组特价!只要998,只要998! ##¥……#……¥%……&%¥……ER#%$#$#^T%$^$% 超级树状数组,其实是一种能够支持区间修改和区间查询的树状数组,和线段树相比,它的常数极小,不需要太多空间,代码量也少了很多(简直吊打线段树) 1.树状数组 既然是超级树状...

weixin_30950887的博客 264

bzoj 2006: [NOI2010]超级钢琴 可持久化线段树+优先队列

题意小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的音乐。 这架超级钢琴可以弹奏出n个音符,编号为1至n。第i个音符的美妙度为Ai,其中Ai可正可负。 一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R。我们定义超级和弦的美妙度为其包含的所有音符的美妙度之和。两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音

beginend 471

推荐——超级详细的线段树讲解

附原文章链接:https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html 数据结构——线段树 O、引例 A.给出n个数,n&lt;=100,和m个询问,每次询问区间[l,r]的和,并输出。 一种回答:这也太简单了,O(n)枚举搜索就行了。 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定。 那好,我再修改一下题目。 B.给出n...

芒果summer小仙女 477

有关线段树合并

目录:一:线段树合并概念二:线段树合并应用。

2509_93020071的博客 2195
上一篇: CDQ分治思想
下一篇: DFS的双向搜索
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值