【树状数组】模板及详解

树状数组详解 

树状数组是一种特殊的数据结构,假设我们有个数组a,建立的树状数组C,能够快速完成下述操作:

  • 计算前缀和,给定 i,计算 a_1+a_2+...+a_i
  • 更改某个位置的值,给定 pos 和 x ,执行  a_i += x
  • 计算区间和,给定区间 l 和 r ,计算  a_l + a_l_+_1 + ... + a_r_-_1 + a_r

需要建立的树状数组如下图所示:

图像摘自: 6954717的博客

得到了树状数组的图,很容易发现:

c[1] = a[1]
c[2] = a[1] + a[2]
c[3] = a[3]
c[4] = a[1] + a[2] + a[3] + a[4]
c[5] = a[5]
c[6] = a[5] + a[6]
c[7] = a[7]
c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]
c[9] = a[9] 

在上述的规律中,其中 c [ x ] 保存的是序列 a 的区间 [ x - lowbit (x) + 1 , x ] 的和。

性质:任意整数的可以被 2 的不重复次幂分解。

比如:7 = 2^2 + 2^1 +2^0 , 8 = 2^3  ,9 = 2^3+2^0 等等……

 所以,若结尾为 R ,则区间长度就等于 R 的 “二进制分解” 下最小的 2 的次幂,即 lowbit(R)

放到树状数组上,即向前取 lowbit(R) 位,就是c的值

lowbit 运算即非负整数下,某个数的二进制表示下最低位的 1 及后续的 0 组成的数值。

深入了解lowbit运算,请戳  ------》 深入理解并证明 lowbit 运算


1 . 计算前缀和

分析:不断向前取位,然后累加即可

int ask(int pos){
	int res = 0;
	while(res){
		res += c[pos];
		pos -= lowbit(pos);
	}
	return pos;
}

2 . 更改某个位置的值

分析:若要更改某个位置的值,因为是树状结构,所以更改完最底层的值,需要向上依次寻找他的父亲,直到 pos 小于总个数即可

void update(int pos,int k){
	while(k<=n){
		c[pos] += k;
		pos += lowbit(pos);
	}
}

3 . 计算区间和

分析:计算区间 [ l , r ] 的和,只需求位置 r 与位置 l - 1 的前缀和,然后相减即可

void query(int l,int r){
	return ask(r) - ask(l-1);
}

下面放一道模板题:【链接】

AC代码:

#include <iostream> 
#include <cstring>
#include <cstdio>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <algorithm>
using namespace std;
#define INF 1 << 30
#define MAXN 500000+5
#define sscc ios::sync_with_stdio(false)
#define mem(a,b) memset(a,b,sizeof(a))
#define mpy(a,b) memcpy(a,b,sizeif(a))
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef unsigned long long ull;

int n,m;
int c[MAXN];

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

int ask(int pos){
	int res=0;
	while(pos){
		res+=c[pos];
		pos-=lowbit(pos);
	}
	return res;
}

int query(int l,int r){
	return ask(r)-ask(l-1);
}

int main(){
	sscc;	
	mem(c,0);
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		int a;
		cin >> a;
		update(i,a);
	}
	while(m--){
		int a,b,c;
		cin >> a >> b >> c;
		if(a==1)
			update(b,c);
		if(a==2)
			cout << query(b,c) << endl; 
	}
	return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值