浅谈树状数组

前言

首先来通过一道题目来展开说明

已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数数加上x
  2. 求出某一个数的值
    数据规模:
    对于30%的数据:N<=8,M<=10
    对于70%的数据:N<=10000,M<=10000
    对于100%的数据:N<=500000,M<=500000

蒟蒻: 暴力修改
dalao: 线段树模板题

这可以用线段树去做,但是线段树的代码量太长了,很容易打错.其实这一题可以用树状数组做.

概念

树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
这种数据结构(算法)并没有C++和Java的库支持,需要自己手动实现。在Competitive Programming的竞赛中被广泛的使用。树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。

来自百度百科
其实树状数组的本质就是进行区间操作,求区间的和,并支持修改操作

算法讲解

树状数组从字面上来解释就是树状的数组

这里的C数组就是树状数组,A数组是原数组
首先将C数组用A数组表示

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[i]=A[i-2k+1]+A[i-2k+2]+......A[i];(k为c数组下标的二进制中从最低位到高位连续零的长度)

但是这个东西我们怎么算呢?

这时候lowbit就很有用了
首先来看看lowbit的代码x&(-x)别看代码只有一行实际上是很有用的,他蕴含着许多深意放屁

实际上lowbit就是计算2^k次方的值
然后你知道这个就应该知道如何用树状数组了

code

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
using namespace std;
int a[500005*3+10],n,m;
int lowbit(int x) {
	return x&(-x);
}
inline void add(int x,int c) {
	while(x<=n) {
		a[x]+=c;
		x+=lowbit(x);
	}
}
inline int sum(int x) {
	int ans=0;
	while(x>0) {
		ans+=a[x];
		x-=lowbit(x);
	}
	return ans;
}
int main() {
	int L,x,y;
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++) {
		scanf("%d",&x);
		add(i,x);
	}
	for(int ii=1; ii<=m; ii++) {
		scanf("%d%d%d",&L,&x,&y);
		if(L==1)
			add(x,y);
		else
			printf("%d\n",sum(y)-sum(x-1));
	}
	return 0;
}
posted @ 2018-10-28 20:33  撤云  阅读(480)  评论(0编辑  收藏  举报
……