「数据结构详解·十五」树状数组

  • 「数据结构详解·一」树的初步
  • 「数据结构详解·二」二叉树的初步
  • 「数据结构详解·三」栈
  • 「数据结构详解·四」队列
  • 「数据结构详解·五」链表
  • 「数据结构详解·六」哈希表
  • 「数据结构详解·七」并查集的初步
  • 「数据结构详解·八」带权并查集 & 扩展域并查集
  • 「数据结构详解·九」图的初步
  • 「数据结构详解·十」双端队列 & 单调队列的初步
  • 「数据结构详解·十一」单调栈
  • 「数据结构详解·十二」有向无环图 & 拓扑排序
  • 「数据结构详解·十三」优先队列 & 二叉堆的初步
  • 「数据结构详解·十四」对顶堆
  • 「数据结构详解·十五」树状数组

0. 前置知识:lowbit 运算

我们定义 lowbit ( x ) \text{lowbit}(x) lowbit(x) x x x 在二进制下最低的 1 1 1 所代表的数。
lowbit ( 101 0 2 ) = 1 0 2 = 2 10 , lowbit ( 1110 1 2 ) = 1 2 = 1 10 \text{lowbit}(1010_2)=10_2=2_{10},\text{lowbit}(11101_2)=1_2=1_{10} lowbit(10102)=102=210,lowbit(111012)=12=110

我们要如何计算这个东西呢?
一种通常的计算方法是 lowbit ( x ) = x & ( ( ∼ x ) + 1 ) = x & − x \text{lowbit}(x)=x\&((\sim x)+1)=x\&-x lowbit(x)=x&((x)+1)=x&x,手模一下可以得到正确性。

现在你学会了计算 lowbit,下面就可以学习树状数组了!

1. 树状数组的概念

树状数组(Binary Indexed Tree, BIT, Fenwick Tree),也称作二叉索引树,是一种维护序列信息的数据结构。所维护的序列信息和运算需要满足一定的要求:

  • 可差分性:即如果知道了 a ∘ b a\circ b ab a a a,可以推得 b b b
  • 结合律:即维护的信息所做的运算 ∘ \circ 满足 ( a ∘ b ) ∘ c = a ∘ ( b ∘ c ) (a\circ b)\circ c=a\circ(b\circ c) (ab)c=a(bc)

在树状数组中,记 bit i \text{bit}_i biti区间 [ i- lowbit( i )+1, i ] \textbf{[\textit{i-}lowbit(\textit i)+1,\textit i]} [i-lowbit(i)+1,i] 的信息和
那么,对于一个序列 a a a 的前缀信息,都被划分成了 log \textbf{log} log
如图所示,底部的点是 a i a_i ai,上方的点是 bit i \text{bit}_i biti

下面以例题具体解释树状数组的实现。

2. 例题详解

2-1. Luogu P3374 【模板】树状数组 1 / Loj 130 树状数组 1 :单点修改,区间查询

思考一下我们修改位置 x x x 的数会影响的到的 bit i \text{bit}_i biti
结合上图的观察,可以发现,如果我们从下往上修改,当修改的是 x x x,则下一次修改的就是 x + lowbit ( x ) x+\text{lowbit}(x) x+lowbit(x)(具体证明留给读者思考)。

而查询 ∑ i = l r a i \sum\limits_{i=l}^ra_i i=lrai 可以拆成 ∑ i = 1 r a i − ∑ i = 1 l − 1 a i \sum\limits_{i=1}^ra_i-\sum\limits_{i=1}^{l-1}a_i i=1raii=1l1ai
显然查询前缀和我们只要不断减去当前的 lowbit \text{lowbit} lowbit 即可。
具体实现:

#include<bits/stdc++.h>
using namespace std;struct fwk{int n,bit[500005];void init(int i){n=i;memset(bit,0,sizeof(bit));}void add(int i,int c){for(;i<=n;i+=i&-i) bit[i]+=c;}int qry(int i){int res=0;for(;i;i-=i&-i) res+=bit[i];return res;}
}bit;int main()
{//freopen(".in","r",stdin);//freopen(".out","w",stdout);int n,m;cin>>n>>m;bit.init(n);for(int i=1;i<=n;i++){int x;cin>>x;bit.add(i,x);}while(m--){int op,x,y;cin>>op>>x>>y;if(op==1) bit.add(x,y);else cout<<bit.qry(y)-bit.qry(x-1)<<endl;}return 0;
}

2-2. Luogu P3368 【模板】树状数组 2 / Loj 131 树状数组 2 :区间修改,单点查询

我们 BIT 只能单点修改啊?怎么办!
其实,只要将序列 a a a 差分成为 b b b 后( b i = a i − a i − 1 b_i=a_i-a_{i-1} bi=aiai1),就变成了单点修改 b l ← b l + x , b r + 1 ← b r + 1 − x b_{l}\gets b_l+x,b_{r+1}\gets b_{r+1}-x blbl+x,br+1br+1x,区间查询 ∑ i = 1 x b i \sum\limits_{i=1}^xb_i i=1xbi 了。
代码和上一题几乎是一样的,所以不再展示了。

2-3. Luogu P3372 【模板】线段树 1 / Loj 132 树状数组 3 :区间修改,区间查询

这就不是很好做了。
首先区间查询就是 ∑ i = 1 r a i − ∑ i = 1 l − 1 a i \sum\limits_{i=1}^ra_i-\sum\limits_{i=1}^{l-1}a_i i=1raii=1l1ai,也就是说我们只要考虑如何求一个前缀和 ∑ i = 1 x a i \sum\limits_{i=1}^xa_i i=1xai
同上一题一样,将 a a a 差分得到 b b b,有这样的推导:
∑ i = 1 x a i = ∑ i = 1 x ∑ j = 1 i b j = b 1 + b 1 + b 2 + b 1 + b 2 + b 3 + ⋯ + b 1 + b 2 + b 3 + ⋯ + b x = ∑ i = 1 x ( x − i + 1 ) b i = ( x + 1 ) ∑ i = 1 x b i − ∑ i = 1 x i ⋅ b i \begin{aligned} &\sum\limits_{i=1}^xa_i\\ =&\sum\limits_{i=1}^x\sum\limits_{j=1}^ib_j\\ =&b_1+\\ &b_1+b_2+\\ &b_1+b_2+b_3+\\ &\cdots+\\ &b_1+b_2+b_3+\cdots+b_x\\ =&\sum\limits_{i=1}^x(x-i+1)b_i\\ =&(x+1)\sum\limits_{i=1}^xb_i-\sum\limits_{i=1}^xi\cdot b_i \end{aligned} ====i=1xaii=1xj=1ibjb1+b1+b2+b1+b2+b3++b1+b2+b3++bxi=1x(xi+1)bi(x+1)i=1xbii=1xibi
发现前后两个和式都是可以用 BIT 维护的!
于是就做完了。代码留给读者实现。

3. 值域树状数组

也称权值树状数组,就是将值的出现情况展现在 BIT 上,类似于哈希表。

以 Luogu P1908 逆序对 为例。
题目要求 i < j i<j i<j a i > a j a_i>a_j ai>aj 的二元组 ( i , j ) (i,j) (i,j) 个数。
考虑一个显然的事情:开一个桶,存放每种数的出现次数 c i c_i ci。那么当加入 a j a_j aj 时,能与 j j j 配对形成 ( i , j ) (i,j) (i,j) i i i 的个数就是 ∑ k = 1 i − 1 c k \sum\limits_{k=1}^{i-1}c_k k=1i1ck
而这个东西恰恰是可以使用 BIT 维护的!
代码也很简单,留给读者实现。

4. 二维树状数组

也称 BIT 套 BIT,也就是把 BIT 放到平面上。

以 Loj 133 二维树状数组 1:单点修改,区间查询 为例。
单点修改是同理的,只要在循环外再套一层就可以。

void add(int x,int y,int d)
{for(int i=x;i<=n;i+=i&-i){for(int j=y;j<=m;j+=j&-j){bit[i][j]+=d;}}
}

区间查询同理。但是注意查询的是 ∑ i = 1 x ∑ j = 1 y a i , j \sum\limits_{i=1}^x\sum\limits_{j=1}^ya_{i,j} i=1xj=1yai,j 的信息,即二维前缀和,减一减即可。

int qry(int x,int y)
{int res=0;for(int i=x;i;i-=i&-i){for(int j=y;j;j-=j&-j){res+=bit[i][j];}}return res;
}
int qry(int a,int b,int c,int d){return qry(c,d)-qry(c,b-1)-qry(a-1,d)+qry(c-1,d-1);}

而 Loj 134 二维树状数组 2:区间修改,单点查询 做二维差分即可。

但是,Luogu P4514 上帝造题的七分钟 / Loj 135 二维树状数组 3:区间修改,区间查询 就不是这样的了。

和一维的同理,令 b i , j = a i , j − a i − 1 , j − a i , j − 1 + a i − 1 , j − 1 b_{i,j}=a_{i,j}-a_{i-1,j}-a_{i,j-1}+a_{i-1,j-1} bi,j=ai,jai1,jai,j1+ai1,j1 后推一个式子:
∑ i = 1 x ∑ j = 1 y a i , j = ∑ i = 1 x ∑ j = 1 y ∑ p = 1 i ∑ q = 1 j b p , q = ∑ i = 1 x ∑ j = 1 y ( x − i + 1 ) ( y − j + 1 ) b i , j = ∑ i = 1 x ∑ j = 1 y ( ( x + 1 ) ( y + 1 ) − ( x + 1 ) j − ( y + 1 ) i + i ⋅ j ) b i , j = ( x + 1 ) ( y + 1 ) ∑ i = 1 x ∑ j = 1 y b i , j − ( x + 1 ) ∑ i = 1 x ∑ j = 1 y j ⋅ b i , j − ( y + 1 ) ∑ i = 1 x ∑ j = 1 y i ⋅ b i , j + ∑ i = 1 x ∑ j = 1 y i ⋅ j ⋅ b i , j \def\s{\sum\limits} \begin{aligned} &\s_{i=1}^x\s_{j=1}^ya_{i,j}\\ =&\s_{i=1}^x\s_{j=1}^y\s_{p=1}^i\s_{q=1}^jb_{p,q}\\ =&\s_{i=1}^x\s_{j=1}^y(x-i+1)(y-j+1)b_{i,j}\\ =&\s_{i=1}^x\s_{j=1}^y((x+1)(y+1)-(x+1)j-(y+1)i+i\cdot j)b_{i,j}\\ =&(x+1)(y+1)\s_{i=1}^x\s_{j=1}^yb_{i,j}-(x+1)\s_{i=1}^x\s_{j=1}^yj\cdot b_{i,j}-(y+1)\s_{i=1}^x\s_{j=1}^yi\cdot b_{i,j}+\s_{i=1}^x\s_{j=1}^yi\cdot j\cdot b_{i,j} \end{aligned} ====i=1xj=1yai,ji=1xj=1yp=1iq=1jbp,qi=1xj=1y(xi+1)(yj+1)bi,ji=1xj=1y((x+1)(y+1)(x+1)j(y+1)i+ij)bi,j(x+1)(y+1)i=1xj=1ybi,j(x+1)i=1xj=1yjbi,j(y+1)i=1xj=1yibi,j+i=1xj=1yijbi,j
于是开 4 4 4 个 BIT 分别维护 b i , j , i ⋅ b i , j , j ⋅ b i , j , i ⋅ j ⋅ b i , j b_{i,j},i\cdot b_{i,j},j\cdot b_{i,j},i\cdot j\cdot b_{i,j} bi,j,ibi,j,jbi,j,ijbi,j 即可。

5.巩固练习

事实上,BIT 还有很多应用,如维护不可差分的信息等。但这些通常是用其他数据结构平替的,故不在此继续做阐述。感兴趣的读者可以自行了解。

  • 上述未给出代码的例题
  • Luogu P2068 统计和
  • Luogu P2357 守墓人
  • Luogu P1774 最接近神的人
  • Luogu P1637 三元上升子序列

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

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

相关文章

如何通过python实现一个web自动化测试框架?

一、首先你得知道什么是Selenium&#xff1f; Selenium是一个基于浏览器的自动化测试工具&#xff0c;它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分&#xff1a;Selenium IDE、Selenium WebDriver和Selenium Grid。 Selenium IDE&#…

[Rust开发]actix_web::middleware 中间件

actix_web::middleware 在 Actix Web 框架中扮演着重要的角色&#xff0c;它允许开发者在处理 HTTP 请求和响应的过程中插入自定义的逻辑。中间件可以在请求到达处理函数之前或响应返回给客户端之前执行&#xff0c;从而实现日志记录、身份验证、数据验证、错误处理等功能。 为…

opencv——图片矫正

图像矫正 图像矫正的原理是透视变换&#xff0c;下面来介绍一下透视变换的概念。 听名字有点熟&#xff0c;我们在图像旋转里接触过仿射变换&#xff0c;知道仿射变换是把一个二维坐标系转换到另一个二维坐标系的过程&#xff0c;转换过程坐标点的相对位置和属性不发生变换&a…

记录:ubuntu24.04源码安装nginx

一. 下载Nginx源码 两个地址二选一即可 Nginx官网Nginx官网 Github eg&#xff1a;nginx-1.27.3.tar.gz 下载到 ubuntu24.04 的 Downloads &#xff0c;解压 cd Downloads tar -zxvf nginx-1.27.3.tar.gz二. 编译安装 Note: 编译最好用 root 权限&#xff0c; 使用下面命令…

CNCF云原生生态版图

CNCF云原生生态版图 概述什么是云原生生态版图如何使用生态版图 项目和产品&#xff08;Projects and products&#xff09;会员&#xff08;Members&#xff09;认证合作伙伴与提供商&#xff08;Certified partners and providers&#xff09;无服务&#xff08;Serverless&a…

wsl2子系统ubuntu发行版位置迁移步骤

默认的wsl2发行版是安装在windos的c盘&#xff0c;占用空间较大&#xff0c;有迁移需求&#xff0c;也可以迁移到其他电脑&#xff1b; 查看现有发行版信息 运行以下命令查看现有的 WSL 发行版及其状态&#xff1a; wsl --list --verbose# 输出示例NAME STATE …

SpringBoot基于Redis+WebSocket 实现账号单设备登录.

引言 在现代应用中&#xff0c;一个账号在多个设备上的同时登录可能带来安全隐患。为了解决这个问题&#xff0c;许多应用实现了单设备登录&#xff0c;确保同一个用户只能在一个设备上登录。当用户在新的设备上登录时&#xff0c;旧设备会被强制下线。 本文将介绍如何使用 Spr…

MVC配置文件及位置

配置文件位置 默认位置 WEB-INF目录下&#xff0c;文件名&#xff1a;<servlet-name>-servlet.xml <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi"http://www.w3.…

【若依项目-RuoYi】掌握若依前端的基本流程

搞毕设项目&#xff0c;使用前后端分离技术&#xff0c;后端springBoot&#xff0c;前端vue3element plus。自己已经写好前端与后端代码&#xff0c;但想换一个前端界面所以使用到了若依&#xff0c;前前后后遇到许多坑&#xff0c;记录一下&#xff0c;方便之后能够快速回忆。…

图像边缘检测示例(综合利用阈值分割、数学形态学和边缘检测算子)

一、问题 读入一副灰度图像&#xff08;如果是彩色图像&#xff0c;可以先将其转化为灰度图像&#xff09;&#xff0c;然后提取比较理想的灰度图像边缘。这里以moon.tif为例。 二、算法 大家一开始容易想到直接利用MATLAB的内置函数edge并采用不同边缘提取算子进行边缘提取&a…

R语言的数据结构-向量

【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言编程_夏天又到了的博客-CSDN博客 在R语言中&#xff0c;数据结构是非常关键的部分&#xff0c;它提…

集成方案 | Docusign + 泛微,实现全流程电子化签署!

本文将详细介绍 Docusign 与泛微的集成步骤及其效果&#xff0c;并通过实际应用场景来展示 Docusign 的强大集成能力&#xff0c;以证明 Docusign 集成功能的高效性和实用性。 在现代企业运营中&#xff0c;效率和合规性是至关重要的。泛微作为企业级办公自动化和流程管理的解决…

Docker Compose应用实战

文章目录 1、使用Docker Compose必要性及定义2、Docker Compose应用参考资料3、Docker Compose应用最佳实践步骤1_概念2_步骤 4、Docker Compose安装5、Docker Compose应用案例1_网站文件准备2_Dockerfile文件准备3_Compose文件准备4_使用docker-compose up启动容器5_访问6_常见…

51c大模型~合集88

我自己的原文哦~ https://blog.51cto.com/whaosoft/12805165 #Number Cookbook 数字比你想得更复杂——一文带你了解大模型数字处理能力的方方面面 目前大语言模型&#xff08;Large Language Models, LLMs&#xff09;的推理能力备受关注。从思维链&#xff08;Chain of…

STP(生成树协议)

STP的基本概念 概述 STP是一个用于局域网中消除环路的协议。运行该协议的设备通过彼此交互信息而发现网络中的环路&#xff0c;并对某些接口进行阻塞以消除环路。STP在网络中运行后会持续监控网络的状态&#xff0c;当网络出现拓扑变更时&#xff0c;STP能够感知并且进行自动…

GLM-4-Plus初体验

引言&#xff1a;为什么高效的内容创作如此重要&#xff1f; 在当前竞争激烈的市场环境中&#xff0c;内容创作已成为品牌成功的重要支柱。无论是撰写营销文案、博客文章、社交媒体帖子&#xff0c;还是制作广告&#xff0c;优质的内容不仅能够帮助品牌吸引目标受众的注意力&a…

Jetpack Compose赋能:以速破局,高效打造非凡应用

Android Compose 是谷歌推出的一种现代化 UI 框架&#xff0c;基于 Kotlin 编程语言&#xff0c;旨在简化和加速 Android 应用开发。它以声明式编程为核心&#xff0c;与传统的 View 系统相比&#xff0c;Compose 提供了更直观、更简洁的开发体验。以下是对 Android Compose 的…

MinerU:PDF文档提取工具

目录 docker一键启动本地配置下载模型权重文件demo.py使用命令行启动GPU使用情况 wget https://github.com/opendatalab/MinerU/raw/master/Dockerfile docker build -t mineru:latest .docker一键启动 有点问题&#xff0c;晚点更新 本地配置 就是在Python环境中配置依赖和…

UE4_控件蓝图_制作3D生命血条

一&#xff1a;效果图如下&#xff1a; 二、实现步骤&#xff1a; 1、新建敌人 右键蓝图类 选择角色&#xff0c; 重命名为BP_Enemytest。 双击打开&#xff0c;配置敌人网格体 修改位置及朝向 效果如下&#xff1a; 选择合适的动画蓝图类&#xff1a; 人物就有了动作&#x…

【深度学习】深刻理解ViT

ViT&#xff08;Vision Transformer&#xff09;是谷歌研究团队于2020年提出的一种新型图像识别模型&#xff0c;首次将Transformer架构成功应用于计算机视觉任务中。Transformer最初应用于自然语言处理&#xff08;如BERT和GPT&#xff09;&#xff0c;而ViT展示了其在视觉任务…