树状数组
有问题欢迎在评论区讨论!
问题引入
题目链接:HDU1166
n个数字,m次操作。
操作分别为:
- Query i j (查询区间 [i, j] 的和)
- Add i j (第i个数字增加j)
- 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[i−2k+1]+a[i−2k+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].
树状数组所做的便是模拟这一规律,将前缀和用这种方式存储在数组中。
实现
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;
}