首页 > 其他分享 >树状数组

树状数组

时间:2023-04-01 15:56:07浏览次数:51  
标签:ch 树状 int text 数组 lowbit

树状数组

简介

树状数组是一种用于维护 \(n\) 个数的区间和的数据结构。
一般能用树状数组做的题,都可以使用线段树来做。相较于码量,树状数组的码量要比线段树少许多,不过相对应的,它所能实现的功能没有线段树多。

好的,不多说废话,下面进入正题。

例题 1:P3374【模板】树状数组 1

例题 2:P3368【模板】树状数组 2

操作

现在我们已知数列 \(a_i\)。

\(\text{lowbit}\)

\(\text{lowbit(x)}\) 表示 \(x\) 表示的二进制中最的一位 \(1\) 所表示的数,举个例子:\(4\) 的二进制为 \(100_{(2)}\),那么 \(\text{lowbit(4)} = 100_{(2)} = 4\);\(12\) 的二进制为 \(1100_{(2)}\),那么 \(\text{lowbit(12)} = 100_{(2)} = 4\)。

怎么求呢?我们知道 \(-x\) 的补码为 \(x\) 按位取反再 \(+1\),大家可以自行举例模拟,会发现最低的一位 \(1\) 所表示的数其实就是 \(x \& (-x)\)。

Code:

int lowbit(int x) {
	return x & (-x);
}

\(\text{lowbit}\) 是树状数组的核心操作,为后面的操作奠定了基础。

\(\text{区间查询}\)

我们令 \(tr_i = \sum_{j = i - \text{lowbit(i)} + 1}^{i} a_i\)

如上图,我们假设要求 \(a_l\) 到 \(a_r\),可以将 \(tr_r\) 加入 \(res\)(query 的值),然后我们就需要求 \(a_l\) 到 \(a_{r - \text{lowbit(r)}}\),再将 \(tr_{r - \text{lowbit(r)}}\) 加入 \(res\dots\)

以此类推,我们就可以得到以下操作。

Code:

int sum(int x) {
	int res = 0;
	for (int i = x; i; i -= lowbit(x) ) res += tr[i];
	return res;
}

答案为 sum(y) - sum(x - 1)

\(\text{单点查询}\)

与 \(\text{区间查询}\) 一样,只不过最后的答案变为了 sum(x)

\(\text{单点修改}\)

修改与查询其实是反着来的,相对应的,\(a_i\) 改变了,\(a_{i + \text{lowbit(i)}}\) 也会跟着改变,那么可以得到以下代码。

Code:

int add(int x,int k) {
	for (int i = x; i <= n; i += lowbit(x) ) tr[i] += k;
}

\(\text{区间修改}\)

这时一定有人会有疑问:单点修改这么做,那区间修改呢?

其实非常简单,假设我们要修改 \(a_l\) 到 \(a_r\),那么我们可以先将 \(a_l\) 到 \(a_n\) 加上 \(k\),再将 \(a_{r + 1}\) 到 \(a_n\) 减去 \(k\),即为我们将 \(a_l\) 到 \(a_r\) 都加上了 \(k\)。(此处以加 \(k\) 为例)

那么区间修改其实就是 add(x,k),add(y + 1,-k)

Code

AC Code of P3374【模板】树状数组 1

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 5e5 + 10;

int n,m;
int tr[N];

inline int read() {
	int x = 0,f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + ch - 48;
		ch = getchar();
	}
	return x * f;
}

int lowbit(int x) {
	return x & (-x);
}

int add(int x,int k) {
	for (int i = x; i <= n; i += lowbit(i) ) tr[i] += k;
}

int sum(int x) {
	int res = 0;
	for (int i = x; i; i -= lowbit(i) ) res += tr[i];
	return res;
}

signed main() {
	ios :: sync_with_stdio(false);
	n = read();
	m = read();
	for (int i = 1; i <= n; ++ i ) add(i,read());
	while (m -- ) {
		int op = read(),x = read(),k = read();
		if (op == 1) add(x,k);
		else cout << sum(k) - sum(x - 1) << endl;
	}
	return 0;
}

AC Code of P3368【模板】树状数组 2

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 5e5 + 10;

int n,m;
int tr[N];

inline int read() {
	int x = 0,f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + ch - 48;
		ch = getchar();
	}
	return x * f;
}

int lowbit(int x) {
	return x & (-x);
}

int add(int x,int k) {
	for (int i = x; i <= n; i += lowbit(i) ) tr[i] += k;
}

int sum(int x) {
	int res = 0;
	for (int i = x; i; i -= lowbit(i) ) res += tr[i];
	return res;
}

signed main() {
	ios :: sync_with_stdio(false);
	n = read();
	m = read();
	for (int i = 1; i <= n; ++ i ) {
		int x = read();
		add(i,x),add(i + 1,-x);
	}
	while (m -- ) {
		int op = read(),x,y,k;
		if (op == 1) {
			x = read(); y = read(); k = read();
			add(x,k);
			add(y + 1,-k);
		} else {
			x = read();
			cout << sum(x) << endl;
		}
	}
	return 0;
}

写 blog 不易,请点个赞。

祝你理解树状数组。

\[\text{Thanks} \]

作者:\(\text{songszh}\)

标签:ch,树状,int,text,数组,lowbit
From: https://www.cnblogs.com/songszh/p/shu-zhuang-shu-zu.html

相关文章

  • 剑指offer42(Java)-连续子数组的最大和(简单)
    题目:输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。示例1:输入:nums=[-2,1,-3,4,-1,2,1,-5,4]输出:6解释: 连续子数组 [4,-1,2,1]的和最大,为 6。提示:1<= arr.length<=10^5-100<=arr[i]<=1......
  • HJ69_矩阵乘法_数组
    思路:三层循环实现矩阵相乘。importsysa=[]forlineinsys.stdin:  a.append(list(map(int,line.strip().split())))#print(a)matrix1=a[3:3+a[0][0]]matrix2=a[3+a[0][0]:]nw=[[0foriinrange(a[2][0])]foriinrange(a[0][0])]forminrange(a[0][0]): ......
  • VBA 对象数组排序算法分享
     FunctionSrotObjectByProperty(objsToSortAsVariant,PropertyNameAsString,Optional降序AsBoolean=True)IfIsEmpty(objsToSort)ThenExitFunctionIfInStr(TypeName(objsToSort),"()")<1ThenExitFunction'IsArray()issom......
  • Java 数组
    数组数组是相同类型数据的有序集合数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们数组的声明和创建首先必须声明数组变量,才能在程序中使用数组。Java语言使用new操作符来创建数组,语......
  • Java 稀疏数组
    稀疏数组当一个数组中大部分元素为0时,或者为同一值的数组时,可以使用稀疏数组来保存该数组。稀疏数组的处理方式是:记录数组一共有几行几列,有多少个不同值把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模下面对该原始数组进行压缩,求出其稀疏数......
  • 【LBLD】小而美的算法技巧:前缀和数组
    【LBLD】小而美的算法技巧:前缀和数组一维数组中的前缀和classNumArray{private:vector<int>preSum;public:NumArray(vector<int>&nums){preSum.push_back(0);for(inti=1;i<nums.size()+1;i++){preSum.push_back(......
  • 一维数组内存分析
    Java虚拟机的内存划分为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。区域名称作用虚拟机栈用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度<br/>的各种基本数据类型、对......
  • 总结的面试题、数组下标为什么从0开始、数组名中存储的是什么、数组的元素如何存储
    系列文章目录文章目录系列文章目录第一题第二题第一题详细解答链接:https://mp.weixin.qq.com/s/N1Mj3DLbFkZeT5hVR05eNA第二题数组的存储:1、数组下标为什么从0开始?下标表示的是这个元素的位置距离首地址的偏移量2、数组名中存储的是什么数组名中存储的是数组在堆中一整块区域......
  • 力扣---剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。 示例:输入:nums= [1,2,3,4]输出:[1,3,2,4]注:[3,1,2,4]也是正确的答案之一。 提示:0<=nums.length<=500000<=nums[i]<=10000来源:力扣(LeetCode)链接:ht......
  • 华为OD机试 合并数组
    本期题目:合并数组题目现在有多组整数数组,需要将他们合并成一个新的数组,合并规则:从每个数组里按顺序取出固定长度的内容,合并到新的数组。取完的内容会删除掉,如果该行不足固定长度,或者已经为空,则直接取出剩余部分的内容放到新的数组中继续下一行。输入第1行为每次读取的固......