28.线段树与树状数组基础

一、线段树

1.区间问题

线段树是一种在算法竞赛中常用来维护区间的数据结构。它思想非常简单,就是借助二叉树的结构进行分治,但它的功能却非常强大,因此在很多类型的题目中都有它的变种,很多题目都需要以线段树为基础进行发展。

具体来讲,线段树可以在 O ( log ⁡ N ) O(\log N) O(logN) 的时间复杂度内实现单点修改和区间修改,以及动态区间查询、求和、求最大、求区间最小值等操作。

2.基本结构

通常我们会将线段树构建成二叉树的样子,二叉树的每一个结点都表示一段区间,并使用数组来进行简化表示。每一个非叶子结点都有左右两棵子树,分别表示区间的左右两部分。现以根节点在数组中的下标为 1 1 1,则线段树具有以下的性质。

  • 一个结点若其在数组中的下标为 p o s pos pos,则它的左右儿子的下标分别为 2 p o s , 2 p o s + 1 2pos,2pos+1 2pos,2pos+1
  • 一个结点表示的区间为 [ l , r ] [l,r] [l,r],则它的左右儿子的表示的区间分别是 [ l , m i d ] , [ m i d + 1 , r ] [l,mid],[mid+1,r] [l,mid],[mid+1,r],其中 m i d = ( l + r ) / 2 mid=(l+r)/2 mid=(l+r)/2
  • 线段树的空间一般要开到 4 n 4n 4n,以防止特殊的越界发生。

例如,以结点总数 n = 10 n=10 n=10 为例构造的线段树如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.线段树的建立

下面以维护区间最小值为例来讲解线段树的基本操作:

  • 函数包含三个参数,分别表示该结点的下标、左端点、右端点。
  • 如果左端点等于右端点,则说明已经到叶子结点了,它的最小值就是它自己,直接对其赋值并返回。
  • 如果不是叶子结点,那么区间 [ l , r ] [l,r] [l,r] 的最小值就是区间 [ l , m i d ] , [ m i d + 1 , r ] [l,mid],[mid+1,r] [l,mid],[mid+1,r] 的最小值的最小值,所以应该递归地先求出子区间的最小值,再最后得到当前的最小值。
ll tree[4*maxn],a[maxn];
void build(ll pos,ll l,ll r)
{if(l==r){tree[l]=a[pos];return;}ll mid=(l+r)>>1;build(pos<<1,l,mid);build(pos<<1|1,mid+1,r);tree[pos]=min(tree[pos<<1],tree[pos<<1|1]);
}

建树的时间复杂度为 O ( n ) O(n) O(n)

4.单点更新

如果这个时候,某一个结点的值被更新了,那么可以考虑这个结点影响了哪些结点,如此只需要在 O ( log ⁡ n ) O(\log n) O(logn) 的时间就可以完成整棵树的更新了,而不需要花费 O ( n ) O(n) O(n) 的时间去重新建树。

思路很简单,我们已知被更新结点的下标,所以只需要判断它在左边还是右边即可,这样每次只选择一边,时间复杂度大大降低。但一定要记住,这里的更新和建树一样,是自下而上更新的,所以结点的值应该在递归的时候更新。

void update(ll pos,ll l,ll r,ll x,ll num)
{if(l==r){tree[pos]=num;return;}ll mid=(l+r)>>1;if(x<=mid)update(pos<<1,l,mid,x,num);elseupdate(pos<<1|1,mid+1,r,x,num);tree[pos]=min(tree[pos<<1],tree[pos<<1|1]);
}

5.单点查询

和单点更新几乎一致

void query(ll pos,ll l,ll r,ll x)
{if(l==r)return tree[pos];ll mid=(l+r)>>1;if(x<=mid)return query(pos<<1,l,mid,x);elsereturn query(pos<<1|1,mid+1,r,x);
}

6.区间查询

因为线段树上的区间划分是固定的,很多时候查询不可能刚好是某一个结点,所以我们需要对区间进行分段,最后依靠递归得到答案。

设我们要查询的区间为 [ s , e ] [s,e] [s,e],则到一个结点时可能有三种情况:

  • 若该结点是 [ s , e ] [s,e] [s,e] 的子区间,那么直接返回这个最小值
  • 若该结点的左半区间与 [ s , e ] [s,e] [s,e] 有交集,即存在一段 [ s , x ] [s,x] [s,x] [ l , m i d ] [l,mid] [l,mid] 有交集,那么只需要满足 s < = m i d s<=mid s<=mid 即可,随后查询左侧的最小值
  • 同理,若该结点的右半区间与 [ s , e ] [s,e] [s,e] 有交集,即存在一段 [ x , e ] [x,e] [x,e] [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 有交集,那么只需要满足 e > m i d e>mid e>mid 即可,随后查询右侧的最小值
  • 最后将左右两侧的最小值取最小值,就是当前区间的最小值
void query(ll pos,ll l,ll r,ll s,ll e)
{if(s<=l && r>=e)return tree[pos];ll mid=(l+r)>>1,ans=inf;if(s<=mid)ans=min(ans,query(pos<<1,l,mid,s,e));if(e>mid)ans=min(ans,query(pos<<1|1,mid+1,r,s,e));return ans;
}

7.区间更新

假设此时,修改的不只是一个元素的值,比如将某一个区间内的所有值都加上 k k k,这个时候就需要区间更新了。如果一个一个更新,无疑是非常糟糕的,还不如重建树。

所以我们可以借助区间查询的思想来进行。但这里有一个问题,区间更新与查询不同,这是会影响到某一整棵子树的值,难道我们要每次都更新到叶子结点吗?

为了优化这一过程,我们引入一个新的数组:懒惰标记。它被定义为当前区间所经历的且还没有向下传递的更新。用以累计这个区间所进行的改变,在需要的时候才向下传递给子结点进行更新。

ll lazy[4*maxn];
void down(ll pos)
{if(lazy[pos]){lazy[pos<<1]+=lazy[pos];lazy[pos<<1|1]+=lazy[pos];tree[pos<<1]+=lazy[pos];tree[pos<<1|1]+=lazy[pos];lazy[pos]=0;}
}
void update(ll pos,ll l,ll r,ll s,ll e,ll k)
{if(s<=l && r<=e){lazy[pos]+=k;tree[pos]+=k;return;}down(pos);ll mid=(l+r)>>1;if(s<=mid)update(pos<<1,l,mid,s,e,k);if(e>mid)update(pos<<1|1,mid+1,r,s,e,k);tree[pos]=min(tree[pos<<1],tree[pos<<1|1]);
}

二、树状数组

三、作业

1.黄题

P3372 【模板】线段树 1

P3870 [TJOI2009] 开关

P1816 忠诚

P1531 I Hate It

P5057 [CQOI2006] 简单题

2.绿题

P3373 【模板】线段树 2

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

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

相关文章

分享一个关于 Go 错误处理的新提案

在比较一段长的时间里&#xff0c;Go 的错误处理已经没有什么特别的进展和新改进了。看着已经到了瓶颈期。 今天在 GitHub 上学习时&#xff0c;看到 Go 社区里有人提了个错误处理的优化提案《proposal: Go 2: Error-Handling Paradigm with !err Grammar Sugar》&#xff0c;…

【PyTorch】(五)模型训练

文章目录 1. 基本步骤 1. 基本步骤 for epoch in range(num_epochs):for _X, _y in dataloader:# 将数据转移到GPU_X, _y _X.to(device), _y.to(device)# 前向传播计算损失loss criterion(model(_X).reshape(_y.shape), _y)# 清空优化器梯度缓存optimizer.zero_grad()# 误差…

SELinux refpolicy详解(7)

接前一篇文章&#xff1a;SELinux refpolicy详解&#xff08;6&#xff09; 三、refpolicy内容详解 1. README 文件路径&#xff1a;refpolicy源码根目录/README。 文件内容如下&#xff1a; 1) Reference Policy make targets:General Make targets:install-src Install …

【Skynet 入门实战练习】分布式 ID | 雪花算法 | 缓存设计 | LRU算法 | 数据库

文章目录 前言雪花算法LRU 算法缓存模块数据库测试逻辑 前言 本节实现了 分布式 ID 生成系统&#xff0c;采用雪花算法实现唯一 ID&#xff1b;实现缓存架构&#xff0c;采用 LRU &#xff08;最近最少使用&#xff09;算法。 雪花算法 分布式 ID 生成算法的有很多种&#x…

Redis——某马点评day01——短信登录

项目介绍 导入黑马点评项目 项目架构 基于Session实现登录 基本流程 实现发送短信验证码功能 controller层中 /*** 发送手机验证码*/PostMapping("code")public Result sendCode(RequestParam("phone") String phone, HttpSession session) {// 发送短信…

mysql8.0 提取json数据转为行

仅适用于mysql8.0&#xff0c;之前的版本不支持JSON_TABLE函数 数据结构: idnamejsonarr1张三[{"daystr": "2023-12-01","score": "53"},{"daystr": "2023-12-02","score": "78"}]2李四[{&q…

B站缓存视频M4S合并MP4(js + ffmpeg )

文章目录 B站缓存视频转MP4&#xff08;js ffmpeg &#xff09;1、说明 2、ffmpeg2.1 下载地址2.2 配置环境变量2.3 测试2.4 转换MP4命令 3、处理程序 B站缓存视频转MP4&#xff08;js ffmpeg &#xff09; 注意&#xff1a;这样的方式只用于个人之间不同设备的离线观看。请…

spring boot mybatis TypeHandler 看源码如何初始化及调用

目录 概述使用TypeHandler使用方式在 select | update | insert 中加入 配置文件中指定 源码分析配置文件指定Mapper 执行query如何转换 结束 概述 阅读此文 可以达到 spring boot mybatis TypeHandler 源码如何初始化及如何调用的。 spring boot 版本为 2.7.17&#xff0c;my…

SQL Server 2016(创建数据表)

1、需求描述。 在名为“class”的数据库中创建表&#xff0c;表名称为“course”&#xff0c;其中要包含序号、课程、课程编号、学分、任课教师、上课地点、开始时间、结束时间、备注等列。 设置各个字段的数据类型。其中&#xff0c;"序号"列为标识列&#xff0c;从…

4-Docker命令之docker kill

1.docker kill介绍 docker kill命令是用来杀掉一个或多个正在运行中的容器。 2.docker kill用法 docker kill [参数] container [container......] [root@centos79 ~]# docker kill --helpUsage: docker kill [OPTIONS] CONTAINER [CONTAINER...]Kill one or more running…

分享几个可以免费使用GPT工具

1. 国产可以使用GPT3.5和4.0的网站&#xff0c;每日有免费的使用额度&#xff0c;响应速度&#xff0c;注册时不用使用手机号&#xff0c;等个人信息&#xff0c;注重用户隐私&#xff0c;好评&#xff01; 一个好用的ChatGPT系统 &#xff0c;可以免费使用3.5 和 4.0https://…

3台4核16G机器搭建K8S集群

在三台具有4核16GB内存的机器上安装Kubernetes&#xff08;K8s&#xff09;集群涉及到几个关键步骤。我们将一个机器设为主节点&#xff08;Master&#xff09;和两个工作节点&#xff08;Worker&#xff09;。以下是详细步骤&#xff1a; 1. 系统环境准备&#xff08;在所有机…

企业如何做好合规管理?

近年来“合规”作为一个热点话题&#xff0c;频繁出现在公众视野&#xff0c;已然成为企业管理发展的大趋势。国家相继出台的各项合规管理标准预示着我国的企业合规管理正逐步从头部央企向民营企业扩展。因此&#xff0c;各大企业将合规管理作为了企业管理的首要任务。 随着中…

Mysql 行转列,把逗号分隔的字段拆分成多行

目录 效果如下源数据变更后的数据 方法第一种示例SQL和业务结合在一起使用 第二种示例SQL和业务结合在一起使用 结论 效果如下 源数据 变更后的数据 方法 第一种 先执行下面的SQL&#xff0c;看不看能不能执行&#xff0c;如果有结果&#xff0c;代表数据库版本是可以的&…

C语言:写一个函数,输入一个十六进制数,输出相应的十进制数

分析&#xff1a; 当用户运行该程序时&#xff0c;程序会提示用户输入一个十六进制数。用户需要在命令行中输入一个有效的十六进制数&#xff0c;例如&#xff1a;"1A3F"。 接下来&#xff0c;程序调用了名为 xbed 的函数&#xff0c;并将用户输入的十六进制数作…

_WorldSpaceLightPos0的含义 UNITY SHADER

_WorldSpaceLightPos0 为当前平行光的方向&#xff0c;方向是从光源到照射的方向。 因此&#xff0c;如果要算发现和平行光之间的夹角&#xff0c; 则需要首先将归一化的_WorldSpaceLightPos0去负数。这样才能继续去计算。 也就是&#xff1a; fixed3 reflectdirnormalize…

php获取时间和MongoDB保存时间不一致

最近遇到了问题&#xff0c;php获取的时间和在MongoDB中查看的时间差了8小时&#xff0c;原因是&#xff1a; PHP 与 MongoDB 的默认时区设置不同&#xff1a; PHP 默认可能使用服务器的本地时区&#xff0c;或者是在 PHP 配置文件&#xff08;php.ini&#xff09;中设置的时…

激光SLAM:Faster-Lio 算法编译与测试

激光SLAM&#xff1a;Faster-Lio 算法编译与测试 前言编译测试离线测试在线测试 前言 Faster-LIO是基于FastLIO2开发的。FastLIO2是开源LIO中比较优秀的一个&#xff0c;前端用了增量的kdtree&#xff08;ikd-tree&#xff09;&#xff0c;后端用了迭代ESKF&#xff08;IEKF&a…

VS2022 配置Qt编译环境 | winows安装Qt5.14.2 | VS2017和Qt5配置成功指南

Visual Studio 2022安装教程完文本内容较多,请耐心看完,挺有收获的,要自己多尝试哦。 文章目录 # 插件安装 如果你想用VS2022来创建QT项目,那么你首先要学会下面的操作,创建一个空白解决方案,在扩展搜索qt,并且下载两个插件(带有绿√的就是)。这里其实是一个坑:VS20…

H5 实现麦克风

要在 HTML5 中使用麦克风&#xff0c;可以使用 Web Audio API 和 getUserMedia() 方法。以下是一个简单的示例&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>使用麦克风</title> </head> <…