数据结构·数状数组(BIT)

树状数组(Binary Index Tree)

英文名:使用二进制下标的树结构

理解:这个树实际上用数组来存,二进制下标就是将正常的下标拆为二进制来看。

求x的最低位1的函数lowbit(x)

  • 假设x的二进制表示为x= ...10000,其中1是x的最低位1 ,前面的位数我们不关心。则-x=.....01111+1=10000(取反+1)。
  • x&-x=...10000&..01111+1=000000...10000,因此我们可以得到x的最低位1所对应的那个数
cpp 复制代码
int lowbit(int pos) {
	return pos & -pos;
}

树状数组的理解

  • t[pos]的含义: t p o s = ∑ { ∀ x ∣ x + l o w b i t ( x ) = p o s } t x + a p o s tpos=\sum_{\{\forall x \mid x+lowbit(x)=pos\}}{tx}+apos tpos=∑{∀x∣x+lowbit(x)=pos}tx+apos
  • 例如: t 8 = 1000 = t 4 = 0100 + t 6 = 0110 + t 7 = 0111 + a 8 t8=1000=t4=0100+t6=0110+t7=0111+a8 t8=1000=t4=0100+t6=0110+t7=0111+a8,其中 a 8 a8 a8是原始数组第8个数。
  • 注意:不能通过 p o s − l o w b i t ( p o s ) pos-lowbit(pos) pos−lowbit(pos)得到x。

建树

  • 由于不能通过 p o s − l o w b i t ( p o s ) pos-lowbit(pos) pos−lowbit(pos)反过来确定x,所以我们要从x开始累加 lowbit(x)向上更新,这一步相当于前缀和的累积。
cpp 复制代码
void build(int pos,int val) {
	while (pos<=n) {
		t[pos] += val;
		pos += lowbit(pos);
	}
}

查询

  • 由于t[pos]的含义不能很好确定 ,因此只能采取笨方法,不断递减 lowbit(pos)获得a[1] to a[pos]的前缀和,然后再来求某一区间的和。
cpp 复制代码
int query(int pos) {
	int sum = 0;
	while (pos >0) {
		sum += t[pos];
		pos -= lowbit(pos);
	}
	return sum;
}

适用问题

前缀和数组支持 O ( 1 ) O(1) O(1)的区间和查询,但是不支持动态的区间修改

差分数组支持 O ( 1 ) O(1) O(1)的区间修改,但是不支持动态的单点求值

树状数组相当于是一个折中,区间修改和查询的复杂度为 O ( l o g n ) O(logn) O(logn)

例题: