-树状数组-

#树状数组
链接: 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值