处理何种问题:常用于动态更新情况下的求区间和,LIS里的求最大值也用到了树状数组。
时间复杂度:单次更新和求和的时间复杂度都是O(logn)
原理:lowbit()函数是整个树状数组的核心,但是在这里我就不说他的理论基础了,只说一下它的功能,例如:给一个原始数组A[]:1,2,3,4,5,6,7,8,9;
被加工之后就是树状数组C[]:1,3,3,10,5,11,7,36,9;即
C1 = A1
C2 = A1+A2
C3 = A3
C4 = A1+A2+A3+A4
C5 = A5
C6 = A5+A6
C7 = A7
C8 = A1+A2+A3+A4+A5+A6+A7+A8
C9 = A9
如下图所示:
树状数组并不是一个二叉树(不像线段树),对于每一个节点,例如C6,我们需要求的是它的父亲节点和前面一个相邻的兄弟节点是哪一个,这些用lowbit()函数都是可以很快捷的找到,即C6的前一个兄弟节点是6-lowbit(6),而他的父亲节点是6+lowbit(6)。
利用如此操作之后,在动态查询修改之后我们不必对全局进行全部修改,只需要更改节点值就可以了,在log级的时间内完成查询和修改。
lowbit(x)=(x&(-x));
实现步骤:具体看代码,略
备注:略
输入样例解释:
10 //数组元素个数
1 2 3 4 5 6 7 8 9 10
2 1 3 // 2代表查询区间[1,3]之和
1 3 6 // 1代表给第3个元素加上6
2 2 7
1 10 -2
1 6 3
2 3 10
输出样例解释:
6 //查询结果
33
59
//对于树状数组,有利有弊,代码也是的确简单,常用在动态更新求区间和 //当然,某些情况下的求解最大值也是可以做的,代码也的确比线段树好写的多 //单点更新的操作可以利用Sum找出单点值,再用Add更新两次即可 #include<iostream> #include<cstdio> #include<string.h> #include<algorithm> using namespace std; #define lowbit(x) (x&(-x)) const int MaxN=10001000; long long arr[MaxN]; int n; void Build() { memset(arr,0,sizeof(arr)); long long temp; int j; for(int i=1; i<=n; ++i) { scanf("%lld",&temp); j=i; while(j<=n) { arr[j]+=temp; j+=lowbit(j); } } } void Add(int x,int val) { while(x<=n) { arr[x]+=val; x+=lowbit(x); } } long long sum(int x) { long long ans=0; while(x>0) { ans+=arr[x]; x-=lowbit(x); } return ans; } int main() { int temp,take; while(~scanf("%d",&n)) { Build();//建树 while(scanf("%d",&take)) { if(take==1)//单点添加 { int x,val; scanf("%d%d",&x,&val); Add(x,val); } else//求区间和 { int l,r; scanf("%d%d",&l,&r); printf("%lld ",sum(r)-sum(l-1)); } } } return 0; }