树状数组是个啥?浅谈树状数组(一)

树状数组

有问题欢迎在评论区讨论!

问题引入

题目链接:HDU1166

n个数字,m次操作。
操作分别为:

  1. Query i j (查询区间 [i, j] 的和)
  2. Add i j (第i个数字增加j)
  3. Sub i j (第i个数字减去j)

使用树状数组

为了简单且高效地解决这个问题,我们需要使用树状数组(当然也可以用线段树)。
线段树传送门:线段树
那么什么是树状数组呢?模拟树的数组就是树状数组~维护的是前缀和。

如图所示。
在这里插入图片描述

需要的变量

为了接下来的介绍,我们先介绍一下需要的变量。

int tree[N], a[N];
// tree[N]: 树状数组
// a[N]   : 原数组

lowbit

我们可(bu)以(neng)发现上图数组满足如下规律:

t r e e [ 1 ] = a [ 1 ] tree[1] = a[1] tree[1]=a[1]
t r e e [ 2 ] = a [ 1 ] + a [ 2 ] tree[2] = a[1] + a[2] tree[2]=a[1]+a[2]
t r e e [ 3 ] = a [ 3 ] tree[3] = a[3] tree[3]=a[3]
t r e e [ 4 ] = a [ 1 ] + a [ 2 ] + a [ 3 ] + a [ 4 ] tree[4] = a[1] + a[2] + a[3] + a[4] tree[4]=a[1]+a[2]+a[3]+a[4]

规律如下:
t r e e [ i ] = a [ i − 2 k + 1 ] + a [ i − 2 k + 2 ] + . . . + a [ i ] tree[i] = a[i - 2^k+1] + a[i - 2^k+2] + ... + a[i] tree[i]=a[i2k+1]+a[i2k+2]+...+a[i]
其中 k k k i i i 的二进制中从最低位到高位连续零的长度。

举个例子:

十进制 二进制 k
4 4 4 100 100 100 2 2 2

故由上述规律可得: t r e e [ 4 ] = a [ 1 ] + a [ 2 ] + a [ 3 ] + a [ 4 ] tree[4] = a[1] + a[2] + a[3] + a[4] tree[4]=a[1]+a[2]+a[3]+a[4].

扫描二维码关注公众号,回复: 11855121 查看本文章

树状数组所做的便是模拟这一规律,将前缀和用这种方式存储在数组中。

实现

lowbit

// 比较简单的一个函数,是树状数组的灵魂所在
int lowbit(int x) {
	// 返回x与上x的二进制中从右向左数第一位1变为0后的数字。
	return x & (-x);
}

add

// 数组下标为 x 的元素增加 k
void add(int x, int k) {
	// 不仅需要tree[x]增加k,也需要所有包含x的tree增加k
	while (x <= n) {
		tree[x] += k;
		// 遍历寻找所有包含x的区间
		x += lowbit(x);
	}
}

query

// 查询区间 [1, x] 的和
int query(int x) {
	int ans = 0;
	// 如上面的图中所示,树状数组中的tree[x]很可能并非[1, x]的区间和,而只是[1, x]的子区间的区间和
	while (x > 1) {
		ans += tree[x];
		// 遍历其它子区间
		x -= lowbit(x);
	}
}

完整代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<deque>
#include<vector>
#include<ctime>

using namespace std;
//#pragma GCC optimize(2)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define ull unsigned long long
#define ll long long
#define rep(i, x, y) for(int i=x;i<=y;i++)
#define mms(x, n) memset(x, n, sizeof(x))
#define mmc(A, tree) memcpy(A, tree, sizeof(tree))
#define INF (0x3f3f3f3f)
#define mod (ull)(1e9+7)
typedef pair<int, int> P;
const int N = 1e5 + 10;
int n;
int tree[N], a[N];
char ch[10];

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

void add(int x, int k) {
    while (x <= n) {
        tree[x] += k;
        x += lowbit(x);
    }
}

int query(int x) {
    int ans = 0;
    while (x >= 1) {
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}

int main() {
//    freopen("input.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    for (int _ = 1; _ <= T; _++) {
        mms(tree, 0);
        mms(a, 0);
        printf("Case %d:\n", _);
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            add(i, a[i]);
        }
        int c, d;
        while (scanf("%s", ch) && ch[0] != 'E') {
            scanf("%d%d", &c, &d);
            if (ch[0] == 'A') {
                add(c, d);
            } else if (ch[0] == 'S') {
                add(c, -d);
            } else if (ch[0] == 'Q') {
                int ans = query(d) - query(c - 1);
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45934120/article/details/108037724
今日推荐