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

news2024/11/18 9:23:50

一、线段树

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);
    else
        update(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);
    else
        return 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.coloradmin.cn/o/1276358.html

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

相关文章

Windows server 2016 FTP服务器的搭建

FTP&#xff08;File Transfer Protocol&#xff09;是一个用来在两台计算机之间传输文件的通信协议。这两台计算机中&#xff0c;一台是FTP服务器&#xff0c;另一台是FTP 客户端。 1.安装FTP服务与建立FTP站点 1.1 打开服务器管理器——单击仪表盘的添加角色和功能 1.2 持续…

Gavin Wood:财库保守主义偏离了初心,应探索 Fellowship 等更有效的资金部署机制

波卡创始人 Gavin Wood 博士最近接受了 The Kusamarian 的采访&#xff0c;分享了他的过往经历、对治理的看法&#xff0c;还聊到了 AI、以太坊、女巫攻击、财库等话题。本文整理自 PolkaWorld 对专访编译的部分内容&#xff0c;主要包含了 Gavin 对治理、财库提案、生态资金分…

听GPT 讲Rust源代码--src/tools(4)

题图由AI生成 File: rust/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs 在Rust源代码中&#xff0c;rust/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs这个文件是rust-analyzer工具的一部分&#xff0c;它定义了用于将类型系统中的实体进行唯一标识和共享…

15.oracle的 listagg() WITHIN GROUP () 行转列函数使用

1.使用条件查询 查询部门为20的员工列表 -- 查询部门为20的员工列表 SELECT t.DEPTNO,t.ENAME FROM SCOTT.EMP t where t.DEPTNO 20 ; 效果&#xff1a; 2.使用 listagg() WITHIN GROUP () 将多行合并成一行(比较常用) SELECT T .DEPTNO, listagg (T .ENAME, ,) WIT…

uniapp小程序分包页面引入wxcomponents(vue.config.js、copy-webpack-plugin)

实例&#xff1a;小程序添加一个源生小程序插件&#xff0c;按照uniapp官方的说明&#xff0c;要放在wxcomponents。后来发现小程序超2m上传不了。 正常的编译情况 会被编译到主包下 思路&#xff1a;把wxcomponents给编译到分包sub_package下 用uniapp的vue.config.js自定义…

【报名】2023产业区块链生态日暨 FISCO BCOS 开源六周年生态大会

作为2023深圳国际金融科技节系列活动之一&#xff0c;由深圳市地方金融监督管理局指导&#xff0c;微众银行、金链盟主办的“2023产业区块链生态日暨FISCO BCOS开源六周年生态大会”将于12月15日下午14:00在深圳举办。 今年的盛会将进一步升级&#xff0c;以“FISCO BCOS和TA的…

C/C++,图算法——求强联通的Tarjan算法之源程序

1 文本格式 #include <bits/stdc.h> using namespace std; const int maxn 1e4 5; const int maxk 5005; int n, k; int id[maxn][5]; char s[maxn][5][5], ans[maxk]; bool vis[maxn]; struct Edge { int v, nxt; } e[maxn * 100]; int head[maxn], tot 1; vo…

Nacos多数据源插件

Nacos从2.2.0版本开始,可通过SPI机制注入多数据源实现插件,并在引入对应数据源实现后,便可在Nacos启动时通过读取application.properties配置文件中spring.datasource.platform配置项选择加载对应多数据源插件.本文档详细介绍一个多数据源插件如何实现以及如何使其生效。 注意:…

动态规划 | 139. 单词拆分、多重背包

139、单词拆分 dp[i]&#xff1a;长度为 i 的字符串可以有字典中出现的单词拼接出来。 if s[j: i] in wordDict and dp[j] true 则 dp[i] true dp[0] true, 因为后续均由dp[0]推出。 从前向后遍历 public static boolean wordBreak(String s, List<String> wordDi…

简单取证-MISC-bugku-解题步骤

——CTF解题专栏—— 题目信息&#xff1a; 题目&#xff1a;简单取证 作者&#xff1a;Tokeii 提示&#xff1a;flag格式为flag{用户名_对应的密码} 例如flag{administrator_bugku} 解题附件&#xff1a; 解题思路&#xff1a; 取证自己属实不太擅长&#xff0c;所以找个题…

JMX的使用

1. 定义和意义 JMX是Java Management Extention的缩写&#xff0c;出发点是让外部通过属性/方法来读取或设置程序状态。对于提供对外服务的程序来说&#xff0c;天生就有这样的能力&#xff0c;Web程序通过HTTP接口对外暴露&#xff0c;RPC应用通过RPC接口暴露。不过带来的问…

如何快速选出一支好股票?

俗话说得好&#xff1a;股票选得好&#xff0c;收益少不了&#xff01;不用多说&#xff0c;相信大伙儿都知道选一支好股票究竟有多重要。 但是选股可不像咱们去菜市场买菜一样&#xff0c;看着顺眼就成。选股&#xff0c;其实是一个专业性特别强的技术活儿。 目前最常用的选股…

微信开发者工具真机调试连接状态在正常和未连接之间反复横跳

开启局域网模式能解决这个问题&#xff0c;目前只找到这一个方法

详解Linux常用命令

目录 1. ps 命令 2. top 命令 3. grep 命令 4. df 命令 5. tail 命令 6. head 命令 7. cat 命令 8. --help 和 man 命令 9. cd 命令 10. mkdir 命令 11. rm 命令 12. mv 和 cp 命令 13. touch 命令 14. vi 或 vim 命令 15. chmod 修改权限 16. 打包和压缩文件 …

电子学会C/C++编程等级考试2021年12月(四级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:移动路线 桌子上有一个m行n列的方格矩阵,将每个方格用坐标表示,行坐标从下到上依次递增,列坐标从左至右依次递增,左下角方格的坐标为(1,1),则右上角方格的坐标为(m,n)。 小明是个调皮的孩子,一天他捉来一只蚂蚁,不小心把…

Windows的常用cmd命令总结

文章目录 一.盘符切换二: cd命令(打开文件/文件夹)三:查看目录四.创建和删除文件夹五.查看本机ip地址六.清除当前屏幕七.复制文件到另一个地方八.移动文件到另一个地方九.删除文件&#xff08;不能删除文件夹&#xff09;十.测试网络连接十一.停止任务进程Windows快捷键总结大全…

python简单进阶之web框架:fastapi使用教程

原文&#xff1a;python简单进阶之web框架&#xff1a;fastapi使用教程 - 知乎 这是简单进阶教程系列第四篇&#xff0c;本系列文章主要介绍那些可以很快上手的进阶库。 我其实学过一段时间Django框架&#xff0c;但是半途而废了&#xff0c;我觉得可能还是简单一点的框架比较适…

瑜伽学习零基础入门,各种瑜伽教学方法全集

一、教程描述 练习瑜伽的好处多多&#xff0c;能够保证平衡健康的身体基础&#xff0c;提升气质、塑造形体、陶冶情操&#xff0c;等等。本套教程是瑜伽的组合教程&#xff0c;共由33套视频教程组合而成&#xff0c;包含了塑身纤体&#xff0c;速效瘦身&#xff0c;四季养生&a…

csp 现值计算 C语言

号&#xff1a; 202212-1 试题名称&#xff1a; 现值计算 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 512.0MB 问题描述&#xff1a; 问题描述 评估一个长期项目的投资收益&#xff0c;资金的时间价值是一个必须要考虑到的因素。简单来说&#xff0c;假设…

010 数据结构_红黑树

前言 本文将会向你介绍红黑树的概念、性质&#xff0c;以及如何手撕红黑树 1 文章重点 文本首先引入红黑树的概念和性质&#xff0c;性质非常重要对于后面的插入操作来说&#xff0c;文章的核心放在了插入部分&#xff0c;另外看插入部分之前记得看声名和节点的定义哦~ 2 引…