题目链接
题目描述
给定一个长度为 的数组,我们需要实现一个数据结构来支持以下两种操作:
- 单点修改:将第
个位置的元素增加
。
- 区间查询:查询区间
内所有元素的和。
输入:
- 第一行包含两个整数
和
,分别表示数组的长度和操作的次数。
- 第二行包含
个整数,表示数组的初始元素。
- 接下来
行,每行描述一个操作。格式为
1 x k
或2 l r
。
输出:
- 对于每个查询操作,输出一行,表示对应区间的元素和。
解题思路
本题是典型的数据结构问题,要求在支持单点修改的同时,能够快速查询区间和。树状数组(Fenwick Tree)是解决此类问题的经典数据结构。
树状数组通过一种巧妙的方式将前缀和存储在数组中,使得单点修改和前缀和查询的时间复杂度都能达到 。
核心思想如下:
-
lowbit
操作:对于一个正整数,
lowbit(x)
定义为的二进制表示中最低位的
所代表的值。例如,
lowbit(6)
(二进制110
) 是2
(二进制10
)。在代码中,它可以通过位运算x & -x
来高效计算。 -
add(x, k)
操作:当我们要将原数组第个元素增加
时,需要更新树状数组中所有包含这个点的区间。这可以通过不断地给
加上
lowbit(x)
来找到所有相关的节点,并更新它们的值。 -
query(x)
操作:查询前缀和(从第个到第
个元素的和),可以通过不断地从
中减去
lowbit(x)
,并将沿途节点的值累加起来得到。 -
区间和查询:对于区间
的和,我们可以利用前缀和的思想来计算,即
query(r) - query(l - 1)
。
通过这几个操作,我们就可以在 的时间内完成所有的操作。
代码
#include <iostream>
#include <vector>
using namespace std;
using LL = long long;
int n;
vector<LL> tr;
int lowbit(int x) {
return x & -x;
}
void add(int x, LL k) {
for (int i = x; i <= n; i += lowbit(i)) {
tr[i] += k;
}
}
LL query(int x) {
LL res = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
int main() {
int m;
cin >> n >> m;
tr.resize(n + 1);
// 初始化树状数组
for (int i = 1; i <= n; i++) {
LL x;
cin >> x;
add(i, x);
}
while (m--) {
int op, x, y;
cin >> op >> x >> y;
if (op == 1) {
add(x, y);
} else {
cout << query(y) - query(x - 1) << "\n";
}
}
return 0;
}
import java.util.Scanner;
public class Main {
static int n;
static long[] tr;
static int lowbit(int x) {
return x & -x;
}
static void add(int x, long k) {
for (int i = x; i <= n; i += lowbit(i)) {
tr[i] += k;
}
}
static long query(int x) {
long res = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
int m = sc.nextInt();
tr = new long[n + 1];
// 初始化树状数组
for (int i = 1; i <= n; i++) {
long val = sc.nextLong();
add(i, val);
}
while (m-- > 0) {
int op = sc.nextInt();
int x = sc.nextInt();
int y = sc.nextInt();
if (op == 1) {
add(x, y);
} else {
System.out.println(query(y) - query(x - 1));
}
}
}
}
import sys
def lowbit(x):
return x & -x
def add(x, k):
while x <= n:
tr[x] += k
x += lowbit(x)
def query(x):
res = 0
while x > 0:
res += tr[x]
x -= lowbit(x)
return res
n, m = map(int, input().split())
a = list(map(int, input().split()))
tr = [0] * (n + 1)
# 初始化树状数组
for i in range(1, n + 1):
add(i, a[i - 1])
for _ in range(m):
line = input().split()
op = int(line[0])
x = int(line[1])
y = int(line[2])
if op == 1:
add(x, y)
else:
print(query(y) - query(x - 1))
算法及复杂度
- 算法:树状数组(Binary Indexed Tree)
- 时间复杂度:初始建树为
,共有
次操作,每次操作为
,总时间复杂度为
。
- 空间复杂度:需要一个数组来存储树状数组,大小为
,所以空间复杂度为
。