【C++】树状数组

文章介绍了树状数组这一数据结构,它是数组的一种优化形式,用于高效处理单点修改和区间查询问题。lowbit概念是树状数组的关键,它表示一个数的最低位1的权值。文章提供了C++代码示例,展示如何使用树状数组进行区间修改和单点查询操作。
摘要由CSDN通过智能技术生成

定义:

所谓树状数组,逻辑结构是一棵树,但是采用数组实现,他能解决单点修改区间查询类的问题,属于前缀和的一种优化。

lowbit:

学习树状数组,首先要引入lowbit 概念,所谓 lowbit,指的是二进制数最低位的1的权值,比如二进制数1100的lowbit就是二进制表示就是100,也就是权值为2^2 = 4,下文中我们用lowbit(x)表示x 的lowbit值。实际上,lowbit(x)= x&(−x),&表示按位与运算。

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

在c语言中,一个数的负数的二进制为该数字取反后加一(-x)等于(~x + 1),其中~符号表示为取反,如某个数的二进制为(10100100),则取反后为(01011011),再加一为(0101100),注意原码,反码,补码的含义。

原码:001010
反码:110101
补码:110110

反码加一后为改数学负数的二进制表达形式

如     x : 1001010110

      ~x :  0110101001

~x + 1 :  0110101010,

从最低位到最高位,如果x的二进制形式下这个位置是0,则取反之后变成1,1 + 1就会往前进位,知道x的某一位为1时,进位结束.因此 lowbit (x):x的二进制形式下最低位的1的权值.

树状数组结构

在图中Ai表示原数组,Ci 表示Ai对应的树状数组。

Ci 维护的是 Ai的区间信息,那么 Ci究竟维护什么区间呢?我们可以先计算下lowbit(i).

lowbit(1)=1
lowbit(2)=2
lowbit(3)=1
lowbit(4)=1
lowbit(5)=1
lowbit(6)=2
lowbit(7)=1
lowbit(8)=8

我们可以发现,Ci 维护的长度就是 lowbit(i),维护区间是 [i−lowbit(i)+1,i]。

如C3维护的区间是[2−lowbit(2)+1,2]即[1,2],C8维护的区间是[8−lowbit(8)+1,8]即[1,8]。

那么树状数组的 Ci 可能会影响哪些位的值呢?以C1为例,1+lowbit(1)=2,2+lowbit(2)=4,4+lowbit(4)=8,我们发现第 i 个数值会递归影响所有的父结点,在树状数组中编号为i 的结点的父结点编号为 i+lowbit(i)。

区间修改与单点查询

#include <bits/stdc++.h>
#define lowbit(x) (x & (-x))
using i64 = long long;
constexpr int N = 1e5 + 10;

template <typename T>
inline void read(T &f) {
    f = 0; T fu = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') { fu = -1; } c = getchar(); }
    while (c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); }
    f *= fu;
}

i64 tree[N], n, m;

void add(int x, int value) {
    for (int i = x; i <= n; i += lowbit(i)) {
        tree[i] += value;
    }
}

i64 sum(int x) {
    i64 ans = 0;
    for (int i = x; i >= 1; i -= lowbit(i)) {
        ans += tree[i];
    }
    return ans;
}

int main() {
    read(n), read(m);
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        read(a[i]);
    }
    for (int i = 1; i <= n; i++) {
        add(i, a[i] - a[i - 1]);
    }
    for (int i = 1; i <= m; i++) {
        int l, r, c;
        read(l), read(r), read(c);
        add(l, c), add(r + 1, -c);
    }
    for (int i = 1; i <= n; i++) {
        printf("%lld%s", sum(i), (i == n ? "\n" : " "));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值