树状数组区间查询区间修改
时间: 2024-08-05 20:01:23 浏览: 83
树状数组,也称为 Fenwick Tree 或 Binary Indexed Tree,是一种用于高效地支持区间查询和更新的数据结构。它主要用于解决范围询问问题,比如求某个区间的元素之和。基本思想是通过二进制操作将数组分为许多小范围,每个节点代表前缀和。
对于区间查询(`sum(a, b)`),你可以从根节点开始,然后依次向下加到`a-1`位置,接着跳过`b`这个节点(因为它的贡献已经被包含在前一个节点),最后返回结果即可。这通常能在线性时间内完成。
区间修改(`update(i, val)`)则相反,你需要从`i`的父节点开始一直加到`n`,每次加上新的值`val`。这种操作同样能在对数时间复杂度内完成。
使用树状数组的优势在于其简洁性和高效的计算性能,特别适合处理大量数据的区间统计问题。
相关问题
树状数组区间修改python代码
下面是实现树状数组区间修改的 Python 代码:
```python
class FenwickTree:
def __init__(self, n):
self.n = n
self.tree = [0] * (n + 1)
def update(self, i, delta):
while i <= self.n:
self.tree[i] += delta
i += i & -i
def query(self, i):
res = 0
while i > 0:
res += self.tree[i]
i -= i & -i
return res
def range_update(self, l, r, delta):
self.update(l, delta)
self.update(r + 1, -delta)
def point_query(self, i):
return self.query(i)
```
其中,`range_update` 方法用于对区间 [l, r] 进行修改,delta 表示增加或减少的值。具体实现是将修改转化为两个单点修改,即将 l 处加上 delta,将 r+1 处减去 delta。`point_query` 方法用于查询某个位置的值。
例如,对于数组 a=[1,2,3,4,5],可以这样使用树状数组区间修改:
```python
tree = FenwickTree(5)
# 将 [2, 4] 区间加上 3
tree.range_update(2, 4, 3)
# 查询第 3 个位置的值
print(tree.point_query(3)) # 输出 5
```
树状数组的区间修改和区间查询
树状数组(Binary Indexed Tree,BIT)是一种数据结构,用于维护序列的前缀和。它可以支持单点修改和区间查询,但是不支持区间修改。不过可以通过巧妙的方式将区间修改转化为单点修改,从而实现区间修改。
区间修改:我们可以将区间修改转化为单点修改,具体方法是将区间修改拆成两次单点修改:先将区间左端点减一,再将区间右端点加一。这样的话,查询时只需要查询右端点的前缀和,再减去左端点的前缀和即可得到区间和。
区间查询:查询区间和可以通过前缀和相减得到。具体方法是查询右端点的前缀和,再减去左端点减一的前缀和即可得到区间和。
具体实现可以参考下面的代码:
```C++
#include <iostream>
#include <vector>
using namespace std;
class BIT {
public:
BIT(int n) : sums(n + 1) {}
void update(int i, int delta) {
while (i < sums.size()) {
sums[i] += delta;
i += lowbit(i);
}
}
int query(int i) {
int sum = 0;
while (i > 0) {
sum += sums[i];
i -= lowbit(i);
}
return sum;
}
private:
vector<int> sums;
int lowbit(int x) {
return x & (-x);
}
};
class RangeBIT {
public:
RangeBIT(int n) : bit1(n), bit2(n) {}
void update(int i, int j, int delta) {
bit1.update(i, delta);
bit1.update(j + 1, -delta);
bit2.update(i, delta * (i - 1));
bit2.update(j + 1, -delta * j);
}
int query(int i, int j) {
return prefix_sum(j) - prefix_sum(i - 1);
}
private:
BIT bit1, bit2;
int prefix_sum(int i) {
return bit1.query(i) * i - bit2.query(i);
}
};
```
注意,这里的 BIT 是用于维护前缀和的,而 RangeBIT 是用于维护区间和的。区间修改时,bit1 用于维护前缀和,bit2 用于维护前缀和乘以下标的和。查询时,先计算出区间的前缀和,再减去左端点减一的前缀和乘以左端点即可得到区间和。
阅读全文