标签:
树状数组也是进行区间操作的常用数据结构。树状数组适用于单个元素经常修改,而且还反复求部分的区间和的情况.
对于数组a,构造一个新的数组C,使得C[i] = a[i-2^k+1] + a[i-2^k+2] + ... + a[i];
(1) i >= 1;
(2) k为i在二进制表示下末尾的连续的0的个数,2^k = i&(-i),通常用lowbit(i)表示i对应的2^k,lowbit(i) = 2^k = i&(-i);
则数组C为数组a的树状数组。
树状数组对应的结构如下图所示:
C[i] = a[i-lowbit(i)+1] + ... + a[i]
C[1] = a[1]
C[2] = a[1] + a[2]
C[3] = a[3]
C[4] = a[1] +a[2] + a[3] + a[4]
树状数组可以快速的求数组a的一个连续区间的和:
sum[k] = a[1] + a[2] + ... + a[k],则a[i] + a[i+1] + a[i+2] + ... +a[j] = sum[j] - sum[i-1]
如果用C数组来表示即为:
sum[k] = a[1] + a[2] + .. a[k] = C[N1] + C[N2] + ... +C[Nm]
其中 Nm = k, Ni-1 = Ni - lowbit(Ni), N1 >=1
//查询 int Query(int p, int n){ int sum = 0; while (p >= 1){ sum += gC[p]; p -= gLowBit[p]; } return sum; }
其中有log(n)个C元素,这就使得树状数组在连续区间求和时,可以达到log(n)的效率。
证明略
对原始数组a中的一个元素ai进行更新,树状有且仅有如下几项会被更新:C[N1], C[N2], ... C[Nm]
其中,N1 = i, Nk+1 = Nk + lowbit(Nk),且Nm <= N
//更新 void Update(int p, int n, bool add){ while (p <= n){ if (add){ gC[p] ++; } else{ gC[p] --; } p += gLowBit[p]; } }
树状数组的应用范围窄,树状数组适用于单个元素经常修改,而且还反复求部分的区间和的情况。所有能用树状数组解决的问题,均可以用线段树解决;
树状数组和线段树的时间复杂度均为O(nlogn),但是树状数组的常数小,效率略高;且树状数组的编程复杂度低。
标签:
原文地址:http://www.cnblogs.com/gtarcoder/p/4786832.html