线段树学习——入门篇

【学习笔记】吉司机线段树 本文同步在洛谷博客上进行发表。 这是一篇刚开始学习线段树的小白都能看懂的良心学习笔记! 前置知识:含有懒标记的线段树(没别的了)。 总述 什么是吉司机线段树? 就是维护区间最值和区间历史最值的线段树,它的名字来源于吉如一老师,他在 201620162016 年发表了一篇集训队论文。 不过为啥这位老师被称为吉司机我也不知道…… 废话不多说,马上进入正题吧。 正题 例题:Luogu 6242 读完题我们发现,一般线段树题目只需要用到一个数组,而这道题用了两个,多出来了个 BBB 数组,是不是要拆成两棵树? 通过 阅读详情

线段树——入门篇

简介

对于广大的OIer来说,线段树并不是一个陌生的词。作为一种比较高大上的数据结构,线段树有许多的优点。它有着过硬的区间处理功能,在解决区间问题上能够占据一席之地,其区间修改和查询的时间复杂度都是O(log2n)的,因此许多区间性质的问题都可以用它来维护以获得良好的效率,而它本身也可以处理许多基础的区间问题。

当然,它也有缺点,最明显的就是它的空间浪费问题,如果是堆式建法(每次把区间划分成尽量等长的两个区间,递归建出一颗完全二叉树)的话,如果恰好建出一颗满二叉树,则正好开到区间原长的两倍,空间复杂是O(2n)。但如果不是这样的话,则需要开到原长的4倍,这样的话是O(4n),但实际用到的只有一半,这样就造成了大量的空间浪费。不过大部分的题目都是允许开到4倍的,如果遇到少数卡空间的题的话,就需要用到动态开点了。

接下来言归正传,开始学习。如图这就是一颗表示区间[1,n]的线段树(摘自度娘),区间中每一部分的信息都从叶子节点一层层的传上来,所以它能够处理各个区间中的情况。每一次修改或者是查询都从根节点开始,会出现三种情况:

(1)需要更改或是查询的区间完全包括了当前的这个节点所表示的区间。此时这个节点所代表的区间将全部作出更改(查询的话返回这个节点的信息)。

(2)需要更改或是查询的区间与当前的这个节点所表示的区间的左半部分有交集。此时就去查找这个节点的左儿子。

(3)需要更改或是查询的区间与当前的这个节点所表示的区间的右半部分有交集。此时就去查找这个节点的右儿子。

(2)、(3)种情况会交替出现,不断查找直到出现(1)情况,然后更改(或返回信息),再一层层的传递上来。这就是线段树的大体工作形式,个人认为理解这些对接下来的学习很有必要。

最基础的线段树能做什么

裸的线段树一般可以解决以下几种操作:

1>建树

在做其他工作之前先建树(有些题目可以不必建树)。

void build(int rt,int l,int r)
{
	if (l==r)
	{
		scanf("%d",&tree[rt]);//读入放在了建树中,也可以在外面读入
		return;
	}
	int m=(l+r)>>1;
	build(lchild); build(rchild);//递归建立左右儿子
	push_up(rt);//维护
}

2>修改:

单点修改:

修改一个点的值:找到它—>修改—>依次更新上来。

void update(int rt,int l,int r,int p,int k)
{
	if (l==r)//找到了需要更新的点
	{
		tree[rt]+=k;//修改
		return;
	}
	int m=(l+r)>>1;
	if (p<=m) update(lchild,p,k);
	else update(rchild,p,k);
	push_up(rt);//更新
}
区间修改:

修改一个区间的值如果再像修改单点一样效率就有些低了,于是需要用到lazy标记(延迟标记)。每次将需要修改的区间打上lazy标记,然后等到下次修改或查询的时候再下放标记,只需要更新用到的点。

void update(int rt,int l,int r,int ll,int rr,int v)
{
	if (ll<=l && r<=rr)//情况(1)
	{
		tree[rt]+=v*(r-l+1);
		lazy[rt]+=v;
		return;
	}
	if (lazy[rt]) push_down(rt,r-l+1);//如果有lazy标记,先下放标记
	int m=(l+r)>>1,k=0;
	if (ll<=m) update(lchild,ll,rr,v);//情况(2)
	if (rr>m) update(rchild,ll,rr,v);//情况(3)
	push_up(rt);//更新
}
3>维护:

区间最值:

如果需要的是区间最值(最大或是最小),那么当前节点的值应在左右儿子的最值中(左右儿子最值中的最值)。下面以最大值为例:

void push_up(int rt)
{
	tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);//"<<"是位运算,是在二进制中左移两位,"<<1" 即乘二,"<<1|1"即乘二加一
}
区间求和:

如果需要的是区间求和,那么当前节点的值是左右儿子的和的和。(...语言有点不通顺,见谅...)

void push_up(int rt)
{
	tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
4>查询

查询操作一般都是区间查询,单点查询可以看成是左右端点相同的区间。下面以查询区间最大值为例:

int query(int rt,int l,int r,int ll,int rr)
{
	if (ll<=l && r<=rr) return tree[rt];//情况(1)
	int m=(l+r)>>1,k=0;
	if (ll<=m) k=max(k,query(lchild,ll,rr));//情况(2)
	if (rr>m) k=max(k,query(rchild,ll,rr));//情况(3)
	return k;
}
以上便是最基础的线段树操作了,掌握了这些就可以做最基本的线段树练习了。最后给出一点小的建议: 最好不要背模板,能够在理解的基础上建立自己的线段树风格是最好的!

练习1.codevs1080线段树练习点击打开链接(单点修改+区间最大值)

练习2.codevs1081线段树练习2点击打开链接(区间修改+单点查询)

练习3.codevs1082线段树练习3点击打开链接(区间修改+区间求和)

练习4.codevs1191数轴染色点击打开链接(区间修改)

练习1.2.3都是裸的线段树操作,4则需要略作思考,不过也是十分基础的线段树,以下为题解:

codevs1191数轴染色

题目描述 Description

在一条数轴上有N个点,分别是1~N。一开始所有的点都被染成黑色。接着
我们进行M次操作,第i次操作将[Li,Ri]这些点染成白色。请输出每个操作执行后
剩余黑色点的个数。

输入描述 Input Description

输入一行为N和M。下面M行每行两个数Li、Ri

输出描述 Output Description

输出M行,为每次操作后剩余黑色点的个数。

样例输入 Sample Input

10 3
3 3
5 7
2 8

样例输出 Sample Output

9
6
3

数据范围及提示 Data Size & Hint

数据限制
对30%的数据有1<=N<=2000,1<=M<=2000
对100%数据有1<=Li<=Ri<=N<=200000,1<=M<=200000

这个题的要求是求出每次染色操作后剩余的黑色点的数量,比较容易构想出线段树做法:

可以开始在建树的时候把每个点都置为1,然后当成区间求和来维护,因为只有两种颜色,所以虽然是区间修改,但却不需要用lazy标记。每次更新完后,输出根节点处的值即为剩余的黑点数。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define lchild rt<<1,l,m
#define rchild rt<<1|1,m+1,r
using namespace std;
int tree[800001]={0},lazy[800001]={0};
void push_up(int rt)
{
	tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void build(int rt,int l,int r)
{
	if (l==r)
	{
		tree[rt]=1;
		return;
	}
	int m=(l+r)>>1;
	build(lchild); build(rchild);
	push_up(rt);
}
void update(int rt,int l,int r,int ll,int rr)
{
	if (tree[rt]==0) return;
	if (ll<=l && r<=rr)
	{
		tree[rt]=0;
		return;
	}
	int m=(l+r)>>1;
	if (ll<=m) update(lchild,ll,rr);
	if (rr>m) update(rchild,ll,rr);
	push_up(rt);
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	build(1,1,n);
	while (m--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		update(1,1,n,x,y);
		printf("%d\n",tree[1]);
	}
	return 0;
}

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

相关推荐

线段树合并与线段树分裂

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

conti123的博客 934

线段树学习

线段树是一种能把一些对于区间(或者线段)的修改、维护,从O(n)的时间复杂度变为O(logn)的工具。线段树是一个完全二叉树,我们可以用堆来存,可以用一维数组来存。对于一个长度为8的线段,可以这样表示每一个线段[L, R],代表区间[L, R]的和,如[1,4]就代表1~4的和如果用一维数组来存,下标(蓝色)如下:由此我们可以知道,对于下标为x的线段,有以下性质以数组a为例:父节点:a[x/2] 或者 a[x >> 1]左儿子:a[2 * x] 或者 a[x

m0_62652820的博客 1876

[学习笔记]线段树骚操作选讲

引入 众所周知,线段树可以维护序列,进行区间操作 单点加 + 区间求和 区间加 + 区间求和 区间加 + 区间乘 + 区间求和 (省略 +∞+\infty+∞ 行) 但有些操作不能像上面三个问题一样通过简单的打标记 + 提取区间解决 而需要用到一些 trick 一、势能线段树 我们知道,线段树能够通过打标记实现区间修改的条件有两个: (1)能够快速处理标记对区间询问结果的影响 (2)能够快速...

女装的你,如此好看! 2152

线段树知识详解

详细讲解线段树基本用法

2302_81988754的博客 1348

线段树学习总结 Part 3 - 线段树 + 离散化

本文是关于 线段树 + 离散化 的讲解 如果你对线段树完全不了解,建树时有一些疑惑,看本文有点不理解,可以试试先看一下 线段树的建树方法与原理 如果你已经对于线段树的基础操作(单点/区间修改,查询)还不太了解,请观看 线段树的一些基础操作 被迫营业 最近这一段时间,嘛~做了一些事情,虽然都在原计划内,但开始的原因还是有点被迫开始的意思。 借口一个被迫之名激励自己做事情,对我来说是挺受用的 =w...

qq_37504214的博客 1233

西门子博图TIA Portal V18实战:用LAD梯形图快速上手一个电机启停控制程序

本文详细介绍了如何在西门子博图TIA Portal V18中使用LAD梯形图快速实现电机启停控制程序。从项目创建、硬件组态到LAD编程和仿真调试,逐步引导读者掌握PLC编程的核心逻辑,特别适合工业自动化领域的初学者快速上手。

weixin_30617737的博客 690

线段树(2)学习

本文为线段树懒标记使用的讲解,是基础线段树内容的续篇。针对线段树区间加操作朴素实现$O(n)$的低效问题,引入懒标记(延迟标记):对完全覆盖的结点打标记并暂停递归修改,仅在后续需深入该结点时下传标记完成子树更新。文章以发工资为喻通俗阐释其原理,给出区间加、标记下传及查询的C++实现代码,证明优化后区间修改与查询的时间复杂度均为$O(\log n)$,文末预告将讲解懒标记的进阶用法。

2501_93313224的博客 871

便携式水下动态实时视频色彩还原设备,一键快速实现对水下偏色视频和图像的色彩还原处理

便携式水下动态实时视频色彩还原,一键快速对水下图像进行色彩校正,清晰度提升和亮度均衡处理,为水下资源勘探和科学考古,海洋生态环境监测,水下机器作业等应用提供有力的协助

weixin_43518680的博客 633

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

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

生命有一种绝对 4万+

allegro转AD-(附工具)

本文介绍了将Allegro的.brd文件转换为Altium Designer可用的.alg文件的操作流程。首先需要下载专用转换工具,然后通过运行.bat脚本将.brd文件转换为.alg格式。最后在Altium Designer中使用导入向导完成文件转换,生成可用的PCB设计文件。整个过程简单快捷,只需三个步骤即可实现Allegro到Altium Designer的工程文件转换。

weixin_50120208的博客 1717

Unity3D实战:5分钟搞定RTSP监控视频播放(附完整代码)

本文提供了一套在Unity3D中快速集成RTSP监控视频播放的实战方案。通过采用LibVLCSharp for Unity这一成熟稳定的跨平台中间件,开发者可以轻松实现RTSP流的解码与渲染,并附有完整的C#代码示例,帮助用户在5分钟内完成集成,适用于智慧园区、安防监控等3D可视化场景。

weixin_29001655的博客 192

线段树入门学习总结

** 线段树 线段树是一种二叉搜索树,与区间树相似,它将一些区间划分成一些单元区间,每个单元区间对应线段树上的一个叶节点。 对于线段树中每个非叶子结点【a,b】,他的左儿子表示的区间为【a,(a+b)/2】,右儿子表示的区间【(a+b)+1,b】,因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。 其时间复杂度为O(m*logN). 线段树有很多模板,基本上每道题都是稍微改下或者...

without的博客 401

用「OpenClaw」打造新媒体运营神器:小红书/公众号全自动选题→创作→排版→发布全流程

对于新媒体运营的从业者来说,AI不是要取代运营人员,而是把他们从繁琐的重复劳动中解放出来,专注于更核心的内容策略、用户运营、品牌建设,这才是AI在传统行业落地的真正价值。这是最终输出可发布内容的核心智能体,我们通过「OpenClaw」的Prompt工程,结合不同平台的排版规范,实现了一键生成符合要求的排版,完全替代人工重复劳动。这套系统的落地,不是技术的炫技,而是真正用AI解决了新媒体运营的核心痛点:用自动化替代重复的人工劳动,用数据决策替代经验主义,用全闭环执行替代纸上谈兵的建议。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 198

线段树(入门篇)

线段树(从入门到深究) 首先说说啥是线段树:线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。 不理解?我们看下图,对于区间1-6,A[1-6]={3,5,1,7,8,2},我们可以建立如下树结构 建线段树我们能干什么? 我们可以用结构体定义每个树的

whilt_hand的博客 836

Arduino ESP8266自定义配置分区表

ESP8266自定义分区表配置 随着项目的不断开发,项目文件也不断在增大,在没有考虑更换主硬件的条件下,最大开发利用有限资源势在必行,所以今天带来ESP8266自定义分区表的配置。 我这里以版本:2.7.4版本为例,后面的版本也是支持的。 注意:自定义分区表特别要注意和你的硬件相对应,flash芯片的大小决定这个分区是否有效,否则有意想不到的事情发现。 自定义分区表的几个重要参数 /* Flash Split for 4M chips */ /* sketch @0x40200000 (~1019K

perseverance51博客 1725

手把手教你学Simulink——基于全桥 LLC 谐振变换器的双向 DC-DC 建模与仿真

本文详细介绍了基于Simulink的双向全桥LLC谐振变换器建模与仿真方法。内容涵盖:1)LLC谐振变换器背景与双向化挑战;2)系统架构设计及关键数学推导(FHA增益分析、双向控制逻辑);3)Simulink建模步骤(功率级搭建、PWM生成、同步整流实现);4)仿真结果分析(正向/反向工作验证、ZVS特性);5)工程实践建议(增益微调、代码生成与HIL测试)。通过手把手教学,帮助读者掌握950V↔48V双向LLC变换器的完整设计流程,实现高效率能量双向流动的同时保持软开关特性,适用于储能系统、车载充电等应用

MHD0815的博客 179

线段树入门篇讲解

总原理: 将[1,n]分解成若干特定的子区间(所开数组的大小不超过4*n,n是原数组的长度) 用线段树统计的东西,必须符合区间加法,(也就是说,如果已知左右两子树的全部信息,比如要能够推出父节点);否则,不可能通过分成的子区间来得到[L,R]的统计结果。 注意:区分3个概念:原数组下标,线段树中的下标和存储下标。 原数组下标,这里都默认下标从1开始(一般用a数组表示,a [1] , a[2...

爬走一切资源的小虫 283

基于STM32L4XX、HAL库的MAX13487EESA RS485总线收发器驱动程序设计

MAX13487EESA是一款3.3V供电的RS-485/RS-422收发器,由Maxim Integrated生产。PA4 (控制引脚) ---> /RE, DE (连接在一起)具有±15kV ESD保护的驱动器输出和接收器输入。半双工RS-485/RS-422通信。工业温度范围:-40°C至+85°C。工作电压范围:3.0V至3.6V。数据速率高达250kbps。

zhusl6688的博客 689

线段树学习笔记 - 动态开点线段树

线段树学习笔记。线段树学习笔记 - 练习题(1)。线段树学习笔记 - 练习题(2)。线段树学习笔记 - 练习题(3)。算法讲解114【扩展】线段树专题5-开点线段树、区间最值和历史最值,题目都是左神视频上的,思路也可以看这个视频。

Jwasx的博客 1194

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【学习笔记】线段树分裂

有就应该有线段树分裂。它是线段树合并的逆过程。具体的,你需要以权值线段树中第 k 小的数为分界线,把线段树分成两半。

Brute♂force 1699

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

线段树详解(原理,实现,应用)入门篇

如果暴力处理区间修改,那么时间复杂度就变成了O(N), 再加上有m次操作,时间复杂度就来到了O(N*M),自然是不优秀的,会被大数据卡掉。这时,引入延迟标记,就使时间复杂度减少到了。这个题就需要两个lazy标记——add,mul, 记住一定要在mul标记时之前的add标记要全部下传,不然就会出问题。这里有一篇blog讲解了为什么,不懂的朋友可以看一看。若还不太熟练,那我建议多去洛谷上刷一些有关线段树的题。这里有一篇比较ok的blog,链接如下。再来一个较难的区间加和区间乘的线段树。

YQY2007123456的博客 140

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【学习笔记】线段树合并

一般来说,线段树会有On个节点。但是有的时候,整棵线段树就只进行了一次插入操作,这样只会有Ologn个节点。处理树上问题时,我们有时需要把儿子的信息合并到父亲节点。这个时候可以使用,常数小,但复杂度多一个log。或者,我们也可以使用线段树合并。

Brute♂force 1624

线段树 (入门篇)

在信息学竞赛中,经常遇到这样一类问题:这类问题通常可以建模成数轴上的问题或是数列的问题,具体的操作一般是每次对数轴上的一个区间或是数列中的连续若干个数进行一种相同的处理。常规的做法一般依托于线性表这种数据结构,导致了处理只能针对各个元素逐个进行,因此算法的效率较低。 线段树是一种能够有效处理区间操作的高级数据结构,利用这种数据结构,我们能够设计出针对上述问题更加高效的算法。一、什么是线段树首先,线

bbbblzy的博客 383

线段树之入门篇

线段树(interval tree) 是把区间逐次二分得到的一树状结构,它反映了包括归并排序在内的很多分治算法的问题求解方式。   上图是一棵典型的线段树,它对区间[1,10]进行分割,直到单个点。这棵树的特点 是: 1. 每一层都是区间[a, b]的一个划分,记 L = b - a 2. 一共有log2L层 3. 给定一个点p,从根到叶子p上的所有区间都包含点p,且其他区间都不包

sxk 2505

算法学习——线段树

线段树的简单操作

SpaceTravellers的博客 1092

李超线段树 & 树链剖分 学习笔记

李超线段树 & 树链剖分 学习笔记(摘自2025年日祭)

一枚初中生的博客 1104
上一篇: UVa 10160 Servicing stations解题报告
下一篇: 网络流学习笔记
morestep
博客等级 码龄12年 20粉丝 88原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值