【刷题】数据结构——树状数组

一、简介

树状数组用于两种操作:

  1. 快速求前缀和 O ( l o g n ) O(logn) O(logn)
  2. 修改某一个数 O ( l o g n ) O(logn) O(logn)

这两个操作也可以用其他方法结构完成:
用一个数组存每个数:操作1. O ( n ) O(n) O(n),遍历前n个数求和;操作2. O ( 1 ) O(1) O(1),直接修改即可
维护前缀和一个数组:操作1. O ( 1 ) O(1) O(1);操作2. O ( n ) O(n) O(n)

可见其他结构最坏情况都是 O ( n ) O(n) O(n)

二、算法

已知一个数可以拆成若干个2的幂次之和
x = 2 i 1 + 2 i 2 + 2 i 3 + . . . + 2 i k x=2^{i_1}+2^{i_2}+2^{i_3}+...+2^{i_k} x=2i1+2i2+2i3+...+2ik
其中 i 1 ≤ i 2 ≤ . . . ≤ i k i_1 \leq i_2 \leq ... \leq i_k i1i2...ik
所以区间 ( 0 , x ] (0, x] (0,x]可以拆成

( x − 2 i 1 , x ] (x-2^{i_1},x] (x2i1,x]
( x − 2 i 1 − 2 i 2 , x − 2 i 1 ] (x-2^{i_1}-2^{i_2},x-2^{i_1}] (x2i12i2,x2i1]
.
.
.
( x − 2 i 1 − 2 i 2 − . . . − 2 i k , x − 2 i 1 − 2 i 2 − . . . − 2 i k − 1 ] (x-2^{i_1}-2^{i_2}-...-2^{i_k},x-2^{i_1}-2^{i_2}-...-2^{i_{k-1}}] (x2i12i2...2ik,x2i12i2...2ik1]
也就是
( 0 , x − 2 i 1 − 2 i 2 − . . . − 2 i k − 1 ] (0,x-2^{i_1}-2^{i_2}-...-2^{i_{k-1}}] (0,x2i12i2...2ik1]

再来看这些区间包含多少个数
( x − 2 i 1 , x ] (x-2^{i_1},x] (x2i1,x]包含 2 i 1 2^{i_1} 2i1个数,其中 i 1 i_1 i1 x x x二进制表示的最后一位1的位置
( x − 2 i 1 − 2 i 2 , x − 2 i 1 ] (x-2^{i_1}-2^{i_2},x-2^{i_1}] (x2i12i2,x2i1]包含 2 i 2 2^{i_2} 2i2个数,其中 i 2 i_2 i2 x − 2 i 1 x-2^{i_1} x2i1二进制表示的最后一位1的位置
( 0 , x − 2 i 1 − 2 i 2 − . . . − 2 i k − 1 ] (0,x-2^{i_1}-2^{i_2}-...-2^{i_{k-1}}] (0,x2i12i2...2ik1]包含 2 i k 2^{i_k} 2ik个数,其中 i k i_k ik x − 2 i 1 − 2 i 2 − . . . − 2 i k − 1 x-2^{i_1}-2^{i_2}-...-2^{i_{k-1}} x2i12i2...2ik1二进制表示的最后一位1的位置。

性质1:对于区间 ( L , R ] (L,R] (L,R],区间包含的数的个数是R的二进制表示的最后一位1的位置对应次幂。

这个最后一位1的位置对应次幂可以用lowbit(x) = x & (-x)计算求得,因为计算机里的负数就是对原数的二进制取反+1

例如: 6 = 11 0 2 6=110_2 6=1102 − 6 = 01 0 2 -6=010_2 6=0102lowbit(6) = 110 & 010 = 10,即6最后一位1对应次幂是 1 0 2 = 2 1 10_2=2^1 102=21

再来看如何拆分区间: x = 6 = 2 2 + 2 1 x=6=2^2+2^1 x=6=22+21,二进制是 11 0 2 110_2 1102,因此 i 1 = 1 i_1=1 i1=1 i 2 = 2 i_2=2 i2=2
所以区间 ( 0 , 6 ] (0, 6] (0,6]可以拆成:
( 6 − 2 1 , 6 ] = ( 4 , 6 ] (6-2^1,6]=(4,6] (621,6]=(4,6],其中 i 1 = 1 i_1=1 i1=1 6 6 6二进制表示 110 110 110最后一位1的位置
( 6 − 2 1 − 2 2 , x − 2 2 ] = ( 0 , 4 ] (6-2^1-2^2,x-2^2]=(0,4] (62122,x22]=(0,4],其中 i 2 = 2 i_2=2 i2=2 4 4 4二进制表示 100 100 100最后一位1的位置

有了性质1,可以得到 L = R − l o b i t ( R ) L=R-lobit(R) L=Rlobit(R),故区间为 ( R − l o b i t ( R ) , R ] (R-lobit(R),R] (Rlobit(R),R]


用数组 t [ x ] t[x] t[x]表示区间 [ x − l o b i t ( x ) + 1 , x ] [x-lobit(x)+1, x] [xlobit(x)+1,x]的和,则 t [ 0 ] t[0] t[0]~ t [ 8 ] t[8] t[8]如下所示
在这里插入图片描述可以看到形成一个类似树的结构,例如t[8]的孩子就有 t [ 4 ] , t [ 6 ] , t [ 7 ] t[4], t[6], t[7] t[4],t[6],t[7]


这个树有如下性质:

性质2:
通过父节点 t [ x ] t[x] t[x]找子节点:令 x ′ = x − 1 x'=x-1 x=x1,之后不断去掉 x ′ x' x的最后一位1即可。
即对于 x = . . . 10...00 0 2 x=...10...000_2 x=...10...0002,得到 x ′ = x − 1 = . . . 01...11 1 2 x'=x-1=...01...111_2 x=x1=...01...1112,所以有孩子 . . . 01...11 1 2 ...01...111_2 ...01...1112 . . . 01...11 0 2 ...01...110_2 ...01...1102 . . . 01...10 0 2 ...01...100_2 ...01...1002 . . . 01...00 0 2 ...01...000_2 ...01...0002、…、 . . . 00...00 0 2 ...00...000_2 ...00...0002

例如 t [ 8 ] t[8] t[8],得到 x ′ = 100 0 2 − 1 2 = 11 1 2 x'=1000_2-1_2=111_2 x=1000212=1112,则孩子有 t [ 11 1 2 ] , t [ 11 0 2 ] , t [ 10 0 2 ] t[111_2],t[110_2],t[100_2] t[1112],t[1102],t[1002]也就是 t [ 7 ] , t [ 6 ] , t [ 4 ] t[7],t[6],t[4] t[7],t[6],t[4]
通过性质2,可以由孩子求出父亲,例如 t [ 8 ] = a [ 8 ] + t [ 4 ] + t [ 6 ] + t [ 7 ] t[8]=a[8]+t[4]+t[6]+t[7] t[8]=a[8]+t[4]+t[6]+t[7]
查询a[1] + ... + a[x] -> for(int i = x; i > 0; i -= lobit(i)) sum += t[i]

性质3:
通过子节点找父节点:将父节点找子节点过程逆过来即可
对于孩子 x = . . . 01...10 0 2 x=...01...100_2 x=...01...1002,父亲一定是 . . . 10...00 0 2 ...10...000_2 ...10...0002,可以看到孩子只有唯一一个父亲
所以只需 . . . 01...10 0 2 + . . . 00...10 0 2 ...01...100_2+...00...100_2 ...01...1002+...00...1002即可得到父亲 . . . 10...000 ...10...000 ...10...000
也就是x + lobit(x)
通过性质3,可以找到更新原数组时所需要更新的对应的树状数组
原数组修改a[x] += c -> 树状数组修改for(int i = x; i <= n; i += lobit(i)) t[i] += c

注意:树状数组维护的位置下表只可以从1开始,如果为0则的lowbit也是0,会死循


三、例题

在这里插入图片描述

树状数组模板

注意

#include <iostream>
using namespace std;const int N = 500005;
int n, m;
int t[N], a[N];int lowbit(int x) {return x & (-x);
}int sum(int x) {int s = 0;for (int i = x; i > 0; i -= lowbit(i))   s += t[i];return s;
}void add(int x, int c) {for (int i = x; i <= n; i += lowbit(i)) {t[i] += c;}
}int main() {scanf("%d%d", &n, &m);for (int i = 1; i <= n; i ++ ) {scanf("%d", &a[i]);add(i, a[i]);}int op, x, y;for (int i = 1; i <= m; i ++ ) {scanf("%d%d%d", &op, &x, &y);if (op == 1) {add(x, y);}else {printf("%d\n", sum(y) - sum(x - 1));}}return 0;
}

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

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

相关文章

Kubernetes(k8s)架构原理

比如在服务器上部署一个博客应用服务,但是太过受欢迎,访问量太大,应用服务经常会挂,使用自动重启工具,并且将应用服务部署在了好几个服务器上,总算抗住了。后来又上线了商城应用服务和语言应用服务,随着应用服务变多,需求也千奇百怪,有的应用服务不希望被外网访问,有…

[Flutter]打包IPA

1.直接使用Xcode运行iOS工程 不用flutter构建&#xff0c;在Xcode中是可以独立进行构建运行和打包发布的。 1).运行项目 先将flutter的build清理 $ flutter clean $ flutter pub get 然后立即用XCode打开iOS工程运行 运行会报错&#xff1a; error: The sandbox is not …

壁纸小程序Vue3(首页布局)

1.创建一个公共目录common来存放css和images App.vue中引用 <style lang"scss"> /*每个页面公共css */ import common/style/common-style.scss; </style> 2.渲染轮播图 <template><view class"homeLayout"><vi…

Godot 4 教程《勇者传说》依赖注入 学习笔记(0):环境配置

文章目录 前言相关地址环境配置初始化环境配置文件夹结构代码结构代码运行 资源文件导入像素风格窗口环境设置背景设置,Tileap使用自动TileMap 人物场景动画节点添加站立节点添加移动动画添加 通过依赖注入获取Godot的全局属性项目声明 当前项目逻辑讲解角色下降添加代码位置问…

【51单片机入门记录】IIC总线协议 EEPROM存储器AT24C02概述

一、IIC总线协议概述 &#xff08;1&#xff09;IIC&#xff08;Inter-IntegratedCircuit&#xff09;总线 是一种由PHILIPS公司开发的两线式串行总线&#xff0c;用于连接微控制器以及其外围设备。IIC也被成为I2C/IC&#xff0c;其实两者是完全相同的&#xff0c;只是名词不…

Linux(CentOS7)配置系统服务以及开机自启动

目录 前言 两种方式 /etc/systemd/system/ 进入 /etc/systemd/system/ 文件夹 创建 nginx.service 文件 重新加载 systemd 配置文件 ​编辑 配置开机自启 /etc/init.d/ 进入 /etc/init.d/ 文件夹 创建 mysql 文件 编写脚本内容 添加/删除系统服务 配置开机自启 …

精通Go语言文件上传:深入探讨r.FormFile函数的应用与优化

1. 介绍 1.1 概述 在 Web 开发中&#xff0c;文件上传是一项常见的功能需求&#xff0c;用于允许用户向服务器提交文件&#xff0c;如图像、文档、视频等。Go 语言作为一门强大的服务器端编程语言&#xff0c;提供了方便且高效的方式来处理文件上传操作。其中&#xff0c;r.F…

9.动态规划——2.最大序列和

例题——最大序列和 找状态 思路一&#xff08;&#xff09; 定义一个状态 d p [ i ] dp[i] dp[i]&#xff0c;计为前i个数构成子序列和的最大值 此法状态转移比较困难&#xff0c;即若 d p [ i ] dp[i] dp[i]与 d p [ i − 1 ] dp[i-1] dp[i−1]没有明确的关系&#xff0c;有…

Ribbon有哪些负载均衡策略

负载均衡类都实现了IRule接口。 RandomRule&#xff1a;随机的选用一个实例 RoundRobinRule&#xff1a;轮询的使用实例 RetryRule&#xff1a;在轮询的基础上加了一个错误重试机制&#xff0c;在deadline时间内会不断的重试 WeightResponeTimeRule&#xff1a;根据权重去做…

【计算机网络】四层负载均衡和七层负载均衡

前言 1、分层方式 首先我们知道&#xff0c;在计算机网络中&#xff0c;常用的协议分层方式&#xff1a;OSI和TCP/IP&#xff0c;以及实际生产中使用的协议划分方式。 在OSI中&#xff0c;各层的职责如下&#xff1a; 应用层&#xff1a;对软件提供接口以使程序能使用网络服…

深入探索位图技术:原理及应用

文章目录 一、引言二、位图&#xff08;Bitset&#xff09;基础知识1、位图的概念2、位图的表示3、位图操作 三、位图的应用场景1、数据查找与存储2、数据去重与排序 四、位图的实现 一、引言 位图&#xff0c;以其高效、简洁的特性在数据处理、存储和检索等多个领域发挥着举足…

Nest安装及使用~

前提条件 请确保您的操作系统上安装了 Node.js&#xff08;版本 > 16&#xff09; &#x1f4da;要查看指南&#xff0c;请访问 https://docs.nestjs.com/ &#x1f4da;要查看中文 指南&#xff0c; 请访问 https://docs.nestjs.cn/ $ node -v v16.18.1 $ npm -v 7.x.x安…

【C++】C++11的新特性

目录 一. 列表初始化1. 列表初始化的原理: initializer_list 二. 类型的声明1. auto2. decltype 三. nullptr四. 范围 for五. STL容器变化六. 类的新功能 一. 列表初始化 在 C 语言中, 就可以使用 {} 对数组或结构体进行初始化, 而 C11 扩大了 {} 的使用范围, 使其可以初始化所…

Mysql-数据库范式和Mysql安装

文章目录 数据库三范式第一范式&#xff1a;1NF第二范式&#xff1a;2NF第三范式&#xff1a;3NF Yum安装CentOS7 yum安装解决“Access denied”拒绝访问异常 数据库三范式 第一范式&#xff1a;1NF 第一范式&#xff1a;数据库中无重复的列&#xff0c;每一列都是不可分割的…

乐乐音乐鸿蒙版-支持krc歌词(动感歌词、翻译和音译歌词)

简介 乐乐音乐主要是基于HarmonyOS开发的音乐播放器&#xff0c;它支持lrc歌词和动感歌词(ksc歌词、krc歌词和hrc歌词等)、多种格式歌词转换器及制作动感歌词、翻译歌词和音译歌词。 开发环境 ArkTS、Stage模型、SDK3.1、 API 9 注&#xff1a;没试过在真机条件下调试。 功…

Java基础学习: JDK动态代理

文章目录 一、什么是JDK动态代理二、JDK动态代理的特点三、JDK动态代理类如何使用四、JDK动态代理原理分析1、创建代理对象2、生成代理类 一、什么是JDK动态代理 JDK动态代理是Java提供的一种动态生成代理类和代理对象的技术。它主要利用Java的反射机制实现&#xff0c;在运行…

Open CASCADE学习|GeomFill_Frenet

GeomFill_Frenet继承自GeomFill_TrihedronLaw类。GeomFill_Frenet类主要用于实现Frenet标架的计算。Frenet标架是一个沿曲线移动的局部坐标系&#xff0c;它由切向量、法向量和副法向量组成&#xff0c;常用于机器人学、计算机图形学等领域。 class GeomFill_Frenet : publi…

docker 数据卷

Docker数据卷是Docker中的一个核心机制&#xff0c;用于实现容器间数据的持久化和共享。它是宿主机上的一个特殊目录&#xff0c;可以供一个或多个容器使用。容器删除时&#xff0c;不会删除其挂载的数据卷&#xff0c;也不会存在类似的垃圾机制对容器存在的数据卷进行处理。 …

每日面经分享(Spring Boot: part2 DAO层)

1. Spring Boot DAO层的作用 a. 封装数据访问逻辑&#xff1a;DAO层的主要责任是封装与数据访问相关的逻辑。负责处理与数据库的交互&#xff0c;包括数据的增删改查等操作。通过将数据访问逻辑统一封装在DAO层中&#xff0c;可以提高代码的可维护性和可重用性。 b. 解耦业务逻…

【vue3学习笔记(二)】(第141-143节)初识setup;ref函数_处理基本类型;ref函数_处理对象类型

尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 本篇内容对应课程第141-143节 课程 P141节 《初识setup》笔记 1、setup是所有组合式API“表演的舞台”&#xff0c;组件中所用到的所有数据、方法、监视数据、生命周期钩子等都需要配置在setup中。 2、setup的两种返回值&…