时隔三年,再次重拾树状数组,不禁想起了那段上课时天天摸鱼的日子,唉唉,虽然后悔自己当时没有好好认真学,但似乎也不可苛责过去的自己。
言归正传,刚刚打了一个最基本的树状数组模板,回顾一下树状数组的原理:
对于一个树状数组,我们初始化其大小为序列长度 ,对于每个位置 ,它维护的是区间 的和,即我们令每个节点负责长度为 的区间和,其中的 怎么取?就使用 的最低位的 的位置,这样的话我们就可以通过 次跳跃来求出某个区间的前缀和或者更新某个位置的值。
模板代码如下:
class fenwick_tree
{
private:
int n;
vector<int> bit;
public:
fenwick_tree(const int n)
{
this->n = n;
bit.resize(n + 1, 0);
}
int lowbit(int x)
{
return x & (-x);
}
void update(int idx, int delta)
{
while (idx <= n)
{
bit[idx] += delta;
idx += lowbit(idx);
}
}
int query(int idx)
{
int sum = 0;
while (idx)
{
sum += bit[idx];
idx -= lowbit(idx);
}
return sum;
}
int query_range(int left, int right)
{
return query(right) - query(left - 1);
}
void initialize(vector<int> &arr)
{
for (int i = 1; i <= n; i++)
update(i, arr[i]);
}
};
值得一提的是,线段树和树状数组在这种特定问题上的效率差距还是比较明显的,尤其是单点更新和求区间前缀和?
可以看到还是有差距的,这是线段树的代码:
class segment_tree
{
private:
int n;
vector<int> tree;
vector<int> vals;
void push_up(int node)
{
int left_node = (node << 1);
int right_node = (node << 1 | 1);
tree[node] = tree[left_node] + tree[right_node];
}
void build(int node, int start, int end)
{
if (start == end)
{
tree[node] = vals[start];
return;
}
int mid = ((start + end) >> 1);
int left_node = (node << 1);
int right_node = (node << 1 | 1);
build(left_node, start, mid);
build(right_node, mid + 1, end);
push_up(node);
}
void update_point(int node, int start, int end, int idx, int val)
{
if (start == end)
{
tree[node] += val;
return;
}
int mid = ((start + end) >> 1);
int left_node = (node << 1);
int right_node = (node << 1 | 1);
if (idx <= mid)
update_point(left_node, start, mid, idx, val);
else
update_point(right_node, mid + 1, end, idx, val);
push_up(node);
}
int query_range(int node, int start, int end, int l, int r)
{
if (start > r || end < l)
return 0;
if (start >= l && end <= r)
return tree[node];
int mid = ((start + end) >> 1);
int left_node = (node << 1);
int right_node = (node << 1 | 1);
return query_range(left_node, start, mid, l, r) + query_range(right_node, mid + 1, end, l, r);
}
public:
segment_tree(const int n, const vector<int> &arr)
{
this->n = n;
vals = arr;
tree.resize((n << 2) + 1);
build(1, 1, n);
}
void update(int idx, int val)
{
update_point(1, 1, n, idx, val);
}
int query(int l, int r)
{
return query_range(1, 1, n, l, r);
}
};
而且树状数组实现也比较简单(