#树状数组
链接: link.
树状数组:用于对于单点修改,区间求和的数据结构
对于一个数组我们想知道某一段的和,可以暴力跑一遍,但这样是很慢的,因为每个点的值只是代表一个点的值,由此我们想到是否能够让一个点的值代表一个小区间的值,然后通过将不同的小区间拼凑在一起就得到需要的大区间的值。
但怎么才能不重不漏的用小区间拼出大区间,区间又该如何定义。这就要用到树状数组这个神奇的结构了。
定义原数组为a,树状数组为tr。假设tr[i] 的 i 对应二进制从后往前数最大连续0为k时,tr[i]表示从i - 2 ^ k + 1 到 i 这个区间的和。
如图 ci表示tr[i]的值连线代表ci包括谁
在这里插入
增加:某一个位置加一个数,只涉及到这个数以及连接它的数都加上这个数,那么谁连接这个数呢,由图可发现,只有它上面一层才连接他,即通过不断地往对应二进制的最后一个1这一位加1使之往上一层跳即可。
查询:例如12这个数对应的二进制为 1100 我们只需要 算 c12 + c8 即可,可以发现满足加法愿意,c12 包括4个数,c8包括8个数,正好是前十二个数,因此只需要每次把最后一个1的这一位减去,再加上新的这一段就可以正好拼出来一个完整的区间了。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <cmath>
#include <stack>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef unsigned long long ULL;
const int N = 5e5 + 10, M = 1100, INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int n, m;
int tr[N], a[N];
int lowbit (int x) {
return x & -x;
}
void add(int x, int v) {
while (x <= n) {
tr[x] += v;
x += lowbit(x);
}
}
int query(int x) {
int res = 0;
while (x >= 1) {
res += tr[x];
x -= lowbit(x);
}
return res;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) {
int x;
cin >> x;
add(i, x);
}
while (m --) {
int op, u, v;
cin >> op >> u >> v;
if (op == 1) add(u, v);
else cout << query(v) - query(u - 1) << endl;
}
return 0;
}
##扩展
树状数组表示的是一段区间的和,我们可以做到单点修改和区间查询。那么如果我们想做到,区间修改和单点查询呢?
这里就要引入前缀和与差分了,前缀和即从开始到现在的和,可以理解数列Sn,而差分即拼成Sn所对应的an,我们树状数组维护一个差分数组,想做到单点查询Sx即把x之前的做一个前缀和,也就是求树状数组1~x的和正好是刚刚把许多小区间拼凑起来的操作。而修改一个区间[l, r],只需将tr[l] + v, tr[r + 1] - v,这样求和的时候只有[l, r]内每一个数加了一个v
链接: link.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <cmath>
#include <stack>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef unsigned long long ULL;
const int N = 5e5 + 10, M = 1100, INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m;
int tr[N], a[N];
int dp[30][2];
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
while (x <= n) {
tr[x] += v;
x += lowbit(x);
}
}
int query(int x) {
int res = 0;
while (x >= 1) {
res += tr[x];
x -= lowbit(x);
}
return res;
}
int main() {
cin >> n >> m;
int last = 0;
for (int i = 1; i <= n; i ++ ) {
int x;
cin >> x;
add(i, x - last);
last = x;
}
while (m --) {
int a, b, c;
cin >> c;
if (c == 1) {
cin >> a >> b >> c;
add(a, c);
add(b + 1, -c);
}
else {
cin >> c;
cout << query(c) << endl;
}
}
return 0;
}
本文介绍了树状数组,它是用于单点修改、区间求和的数据结构。通过让点的值代表小区间的值,可拼凑出大区间的值。文中说明了树状数组的定义、增加和查询操作,还介绍了其扩展应用,引入前缀和与差分实现区间修改和单点查询。
1244

被折叠的 条评论
为什么被折叠?



