刘汝佳の树状数组详解

news2024/10/7 10:24:57

引入

二叉索引树,也叫树状数组是一种便于数组单点修改和区间求和的数据结构
主要根据下标的lowbit值来建树
至于lowbit(x),则是(x)&(-(x)),也就是一个二进制数从右边数第一个1代表的数

#define lowbit(x) ((x)&(-(x)))

基础树状数组如下图所示
在这里插入图片描述
灰色结点为树状数组中的结点,不难发现,lowbit值相同的结点在同一层上,lowbit值越大越靠近根
我们用一个c数组来存储图中白色长条(最下面结点所代表的区间和就是其本身)的区间和
不难看出
c i = ∑ j = i − l o w b i t ( i ) i a j c_i=\sum_{j=i-lowbit(i)}^ia_j ci=j=ilowbit(i)iaj
在有了以上理论基础后,我们来研究一下树状数组中的相关操作

树状数组的相关操作

单点修改

当我们要修改树状数组中的其中一个结点时,我们不仅需要改这个单点的值,还要修改c数组的值(也就是白色长条所代表的值),我们发现,这个结点只有其正上方的白色长条覆盖着,所以只需要一边往上爬一边修改沿路的c就可以了,如图所示
在这里插入图片描述

代码

void add(int x,int k){
	while(x<=n){
		c[x]+=k;//对沿路的c数组进行修改
		x+=lowbit(x);//往上爬,若是这一操作无法理解的话,推荐读者打一下草
	}
}

求前缀和

为了求一段前缀和,我们可以在图中发现我们需要往左上一直爬并加上沿路的c数组
如图所示
在这里插入图片描述

代码

int query(int x){
	int sum=0;
	while(x>0){
		sum+=c[x];//加上沿路的c数组
		x-=lowbit(x);//往左上爬
	}
	return sum;
}

区间修改与区间和

接下来讲解的就是树状数组中比较难理解的区间操作
区间操作需要的是差分
注:接下来的操作推荐读者与笔者一同推算
d i f 1 = a 1 , d i f 2 = a 2 − a 1 , d i f 3 = a 3 − a 2 . . . d i f i = a i − a i − 1 dif_1=a_1,dif_2=a_2-a_1,dif_3=a_3-a_2...dif_i=a_i-a_{i-1} dif1=a1,dif2=a2a1,dif3=a3a2...difi=aiai1
特别地, a 0 = 0 a_0=0 a0=0
如果用dif表示出a来,那么可以发现 a i = ∑ j = 1 i d i f j a_i=\sum_{j=1}^idif_j ai=j=1idifj
所以得出前缀和
s u m i = ∑ j = 1 i ∑ k = 1 k d i f k sum_i=\sum_{j=1}^i\sum_{k=1}^kdif_k sumi=j=1ik=1kdifk
= ( d i f 1 ) + ( d i f 1 + d i f 2 ) + . . . + ( d i f 1 + d i f 2 + . . . + d i f i ) =(dif_1)+(dif_1+dif_2)+...+(dif_1+dif_2+...+dif_i) =(dif1)+(dif1+dif2)+...+(dif1+dif2+...+difi)
= i ∗ d i f 1 + ( i − 1 ) ∗ d i f 2 + . . . + 1 ∗ d i f i =i*dif_1+(i-1)*dif_2+...+1*dif_i =idif1+(i1)dif2+...+1difi
= i ∗ ( d i f 1 + d i f 2 + . . . + d i f i ) − ( 0 ∗ d i f 1 + 1 ∗ d i f 2 + . . . ( i − 1 ) ∗ d i f i ) =i*(dif_1+dif_2+...+dif_i)-(0*dif_1+1*dif_2+...(i-1)*dif_i) =i(dif1+dif2+...+difi)(0dif1+1dif2+...(i1)difi)
= i ∗ ∑ j = 1 i d i f j − i ∗ ∑ j = 1 i ( j − 1 ) ∗ d i f j =i*\sum_{j=1}^idif_j-i*\sum_{j=1}^i(j-1)*dif_j =ij=1idifjij=1i(j1)difj
然后分别用树状数组T1,T2存这两项
那么在区间修改时,如何对T1,T2修改呢?举个例子
对序列 1 , 2 , 3 , 4 , 5 1,2,3,4,5 1,2,3,4,5的[2,4]进行+1操作
操作后的序列为 1 , 3 , 4 , 5 , 5 1,3,4,5,5 1,3,4,5,5
原序列的查分序列为 1 , 1 , 1 , 1 , 1 1,1,1,1,1 1,1,1,1,1
而操作后的查分序列为 1 , 2 , 1 , 1 , 0 1,2,1,1,0 1,2,1,1,0
可以看到,只需要更改第 l l l项和第 r + 1 r+1 r+1项即可,再对应到T1,T2的具体含义中,就可以进行修改操作了
区间[l,r]和
s u m r − s u m l − 1 sum_r-sum_{l-1} sumrsuml1
= [ r ∗ ∑ j = 1 r d i f j − r ∗ ∑ j = 1 r ( j − 1 ) ∗ d i f j ] − [ ( l − 1 ) ∗ ∑ j = 1 l − 1 d i f j − ( l − 1 ) ∗ ∑ j = 1 l − 1 ( j − 1 ) ∗ d i f j ] =[r*\sum_{j=1}^rdif_j-r*\sum_{j=1}^r(j-1)*dif_j]-[(l-1)*\sum_{j=1}^{l-1}dif_j-(l-1)*\sum_{j=1}^{l-1}(j-1)*dif_j] =[rj=1rdifjrj=1r(j1)difj][(l1)j=1l1difj(l1)j=1l1(j1)difj]

代码

struct BIT{
	static const int M=1e5+5;
	int c[M];
	void add(int x,int k) { while(x<=n) c[x]+=k,x+=lowbit(x); }
	int query(int x) { int sum=0;while(x>0) sum+=c[x],x-=lowbit(x);return sum; }
}T1,T2;
//区间修改
void build(int x,int dif){
	T1.add(x,dif);
	T2.add(x,(x-1)*dif);
}
void interval_add(int l,int r,int k){
	T1.add(l,k),T1.add(r+1,-k);
	T2.add(l,k*(l-1)),T2.add(r+1,-k*(r+1-1));
}
//区间查询
void interval_query(int l,int r){
	return (r*T1.query(r)-(l-1)*T1.query(l-1))-(T2.query(r)-T2.query(l-1));
}

例题

P3374 【模板】树状数组 1

在这里插入图片描述

思路

这题的数据还是比较水的,所以笔者直接暴力用前缀和过了

代码

#include<bits/stdc++.h>
using namespace std;
const int M=1e6+5;
#define int long long
#define lowbit(x) (x)&(-(x))
int n,m;
int c[M];
void add(int x,int k) { while(x<=n) c[x]+=k,x+=lowbit(x); }
int query(int x) { int sum=0;while(x>0) sum+=c[x],x-=lowbit(x);return sum; }
signed main()
{
	cin>>n>>m;
	for(int i=1,p;i<=n;i++) cin>>p,add(i,p);
	while(m--){
		int if_case;cin>>if_case;
		int x,y,k;
		switch (if_case){
			case 1:cin>>x>>k;add(x,k);break;
			case 2:cin>>x>>y;cout<<query(y)-query(x-1)<<endl;break;//暴力前缀和做法
		}
	}
	return 0;
}

P3368 【模板】树状数组 2

在这里插入图片描述

思路

这个题就需要区间操作了,需要的是区间修改
我们因为T1和T2存的都是查分,所以需要加一点操作

a i = ∑ j = 1 i d i f j a_i=\sum_{j=1}^idif_j ai=j=1idifj

所以查询第x个数时直接输出T1的前缀和就行
理论存在,实践开始

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lowbit(x) ((x)&(-(x)))
int n,m;
struct BIT{
	static const int M=5e5+5;
	int c[M];
	void add(int x,int k) { while(x<=n) c[x]+=k,x+=lowbit(x); }
	int query(int x) { int sum=0;while(x>0) sum+=c[x],x-=lowbit(x);return sum; }
}T1,T2;
void build(int x,int dif) { T1.add(x,dif),T2.add(x,(x-1)*dif); }
void interval_add(int l,int r,int k){
	T1.add(l,k),T1.add(r+1,-k);
	T2.add(l,k*(l-1)),T2.add(r+1,-k*(r+1-1));
}
int query(int x) { return T1.query(x); }
signed main()
{
	cin>>n>>m;
	int a,c=0;
	for(int i=1;i<=n;i++){
		cin>>a;
		c=a-c;
		build(i,c);
		c=a;
	}
	while(m--){
		int if_case;
		cin>>if_case;
		int x,y,k;
		switch (if_case){
			case 1:cin>>x>>y>>k;interval_add(x,y,k);break;
			case 2:cin>>x;cout<<query(x)<<endl;break;
		}
	}
}

end

参考资料:刘汝佳·《算法竞赛入门经典训练指南》
就这样,树状数组讲解完了,笔者不多赘述
完结撒花

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

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

相关文章

GF(2)上矩阵秩的快速计算

https://github.com/mhostetter/galois/issues

uniapp发布插件显示components/xxx文件没找到,插件格式不正确

uniapp发布插件显示components/xxx文件没找到&#xff0c;插件格式不正确 将插件文件这样一起选中&#xff0c;然后右键压缩成zip文件&#xff0c;而不是外层文件压缩

亚马逊、美客多卖家如何运营,养号技巧和硬件要求有哪些?

流量等于销量、等于利润&#xff0c;没有流量&#xff0c;一切都是白搭&#xff0c; 流量是一切销量的前提&#xff0c;我们平时做的优化、推广也是为了引入流量。所有亚马逊卖家都在围着一个目标而努力&#xff0c;那就是流量。那么亚马逊新卖家该如何引流呢? 我们需要从以下…

无涯教程-Perl - 条件判断

以下是在大多数编程语言中找到的典型判断结构的概述- Perl编程语言提供以下类型的条件语句。 Sr.No.Statement & 描述1 if statement if语句由布尔表达式和一个或多个语句组成。 2 if...else statement在 if语句之后可以是可选的 else语句。 3 if...elsif...else statemen…

如何将镜像体积海量缩减

点击上方蓝色字体&#xff0c;选择“设为星标” 回复”云原生“获取基础架构实践 镜像的传统构建 我们随便找个Golang代码项目作为案例&#xff0c;来开始构建一个镜像。下面我们以我的一个实战项目开始讲解&#xff1a;https://gitee.com/damon_one/uranus。 第一步&#xff1…

uC-OS2 V2.93 STM32L476 移植:系统移植篇

前言 上一篇已经 通过 STM32CubeMX 搭建了 NUCLEO-L476RG STM32L476RG 的 裸机工程&#xff0c;并且下载了 uC-OS2 V2.93 的源码&#xff0c;接下来&#xff0c;开始系统移植 开发环境 win10 64位 Keil uVision5&#xff0c;MDK V5.36 uC-OS2 V2.93 开发板&#xff1a;NUC…

机器学习深度学习——从全连接层到卷积

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——非NVIDIA显卡怎么做深度学习&#xff08;坑点排查&#xff09; &#x1f4da;订阅专栏&#xff1a;机器…

D. Different Arrays

Problem - 1783D - Codeforces 思路&#xff1a; 这是一个计数问题&#xff0c;我们要统计不同数组的个数&#xff0c;可以用dp&#xff0c;让f[i][j]表示只考虑前i个&#xff0c;并且结尾为j的情况&#xff0c;那么转移方程为我们枚举i&#xff0c;与枚举前一个是多少&#xf…

电脑安装新系统不知道去哪里下载,看我就够了

大家在日常生活中肯定都会遇到电脑安装系统的需求&#xff0c;如果去微软官方购买正版的系统又很贵&#xff0c;又不太想花这个冤枉钱&#xff0c;这个时候我们就不得不去网上查找一些免费好用的系统&#xff0c;可是百度一下&#xff0c;或者Google一下&#xff0c;各种下载系…

【css】css设置表格样式-边框线合并

<style> table, td, th {border: 1px solid black;//设置边框线 }table {width: 100%; }td {text-align: center;//设置文本居中 } </style> </head> <body><table><tr><th>Firstname</th><th>Lastname</th><t…

【uniapp】样式合集

1、修改uni-data-checkbox多选框的样式为单选框的样式 我原先是用的单选&#xff0c;但是单选并不支持选中后&#xff0c;再次点击取消选中&#xff1b;所以我改成了多选&#xff0c;然后改变多选样式&#xff0c;让他看起来像单选 在所在使用的页面上修改样式即可 <uni-d…

1-搭建一个最简单的验证平台UVM,已用Questasim实现波形!

UVM-搭建一个最简单的验证平台&#xff0c;已用Questasim实现波形 1&#xff0c;背景知识2&#xff0c;".sv"文件搭建的UVM验证平台&#xff0c;包括代码块分享3&#xff0c;Questasim仿真输出&#xff08;1&#xff09;compile all&#xff0c;成功&#xff01;&…

kubernetes 集群利用 efk 收集容器日志

文章目录 [toc]前情提要制作 centos 基础镜像准备 efk 二进制文件部署 efk 组件配置 namespace配置 gfs 的 endpoints配置 pv 和 pvc部署 elasticsearchefk-cmefk-svcefk-sts 部署 filebeatfilebeat-cmfilebeat-ds 部署 kibanakibana-cmkibana-svckibana-dp使用 nodeport 访问 …

免费快速下载省市区县行政区的Shp数据

摘要&#xff1a;一般非专业的GIS应用通常会用到省市等行政区区划边界空间数据做分析&#xff0c;本文简单介绍了如何在互联网上下载省&#xff0c;市&#xff0c;区县的shp格式空间边界数据&#xff0c;并介绍了一个好用的在线数据转换工具&#xff0c;并且开源。 一、首先&am…

图卷积网络(GCN)和池化

一、说明 GCN&#xff08;Graph Convolutional Network&#xff09;是一种用于图形数据处理和机器学习的神经网络架构。GCN 可以在图形中捕获节点之间的关系&#xff0c;从而能够更好地处理图形数据。GCN 可以沿着图形上的边缘执行滤波器操作&#xff0c;将每个节点的特征向量进…

OA是什么意思?OA系统是什么

阅读本文您可以了解&#xff1a;1、OA是什么&#xff1b;2、OA系统是什么&#xff1b;3、OA系统有什么功能 一、OA是什么 当提到OA&#xff0c;我们通常指的是"Office Automation"&#xff08;办公自动化&#xff09;。 办公自动化指的是在办公室环境中使用计算机技…

培训系统中的技术创新与发展趋势

随着互联网和信息技术的快速发展&#xff0c;培训系统也进入了一个全新的时代。传统的面对面培训逐渐被在线培训所取代&#xff0c;培训系统中的技术创新和发展成为了推动整个行业发展的关键因素。 1. VR和AR技术的应用 虚拟现实&#xff08;VR&#xff09;和增强现实&#x…

并查集和哈希表的实现

并查集和哈希表的实现 文章目录 并查集和哈希表的实现1.并查集的功能2.并查集的基本原理3.并查集的实现 哈希表&#xff08;hash&#xff09;1.拉链法2.开放寻址法方法流程代码演示 3,字符串哈希 1.并查集的功能 1.将两个集合进行合并 2.询问两个元素是否在一个集合里面 2.并…

js中exec与match的区别

const regex1 RegExp(f(o.?),g); const str1 table foatball, fobsball; let array1; let array2; array1 regex1.exec(str1) array2 str1.match(regex1)console.log(array1, array1); console.log(array2, array2); //没有g的情况下,都是找到第一个匹配,并且如果有分组,…

SPSS数据分析--假设检验的两种原假设取舍决定方式

假设检验的两种原假设取舍决定方式 在t检验&#xff0c;相关分析&#xff0c;回归分析&#xff0c;方差分析&#xff0c;卡方检验等等分析方法中&#xff0c;都需要用到假设检验。假设检验的步骤一般如下&#xff1a; 提出假设&#xff1a;H0 vs H1 ;假设原假设H0 成立的情况…