高级数据结构——树状数组

高级数据结构——树状数组

  • 一维树状数组
  • 二维树状数组
  • 改进:区间更新-单点求值(一维差分)
  • 改进:区间更新-单点求值(二维差分)

一维树状数组

一维树状数组中,我们维护了一个前缀和。 例如对于bit[i],我们维护了从A[i-lowbit(i)+1]到A[i]的前缀和。

那么要求到A[i]的前缀和,只要不断的让i减 lowbit(i),通过求出不同区间的前缀和,从而求出从0开始到i的和。

加上一个数x的操作也是类似,比如对A[i]加上x,那么就要对所有包含元素A[i]的区间进行更新,让i加上lowbit(i)就可以。注意树状数组下标从1开始。

下面是对于树状数组的两种操作

int lowbit(int i) {return i & -i;}

void add(int i, int x) {
	while (i <= n) {
		bit[i] += x;
		i += lowbit(i);
	}
}

int sum(int i) {
	int s = 0;
	while (i > 0) {
		s += bit[i];
		i -= lowbit(i);
	}
	return s;
}

二维树状数组

那么对于二维的树状数组同样也是这样。
对于BIT[i][j],维护的是从A[i-lowbit(i)+1][j-lowbit(j)+1]到A[i][j]的前缀和。
如果想要求到位置(i,j)的前缀和,只需要进行如下改动:

void add(int i, int j, int x) {
	while (i <= n) {
		int j_ = j;
		while (j_ <= n) {
			bit[i][j_] += x;
			j_ += j_ & -j_;
		}
		i += i & -i;
	}
}

int sum(int i, int j) {
	int s = 0;
	while (x > 0) { 
		int j_ = j_;
		while (ty <= n) {
			s += bit[x][j_];
			j_ -= j_ * -j_;
		}
		i -= i & -i;
	}
	return s;
}

改进-区间更新-单点求值(一维差分)

对于普通的树状数组,只能进行单点更新,那么如果要进行区间更新呢。比如要对从i到j的元素同时加上x,然后求任意位置在操作后的值。

我们可以通过树状数组的方式维护一个差分数组
形式如下: diff[i] = A[i] - A[i-1]

那么如果要求任一元素的值,就可以通过下面计算得到:
A[i] = ∑diff[j] (j 从0到i)

这样我们就可以不需要关注区间内每个元素的具体值,只需要关注每两个相邻元素之间的差值。那么这么做的好处是什么呢。

考虑如下元素: [1,5,2,7,5],其差分数组为:[1, 4, -3, 5, -2] (第一个元素为与0的差分,因为树状数组不需要用到第0个元素,所以第0个元素永远为0)

如果要对区间[2, 4]的元素每个加上2,则注意其差分数组的变化:
[1, 6, -3, 5, -4],容易发现,在区间内部的元素由于同时加上了一个数,其差值不变,唯一改变的是位于位置2(i)和位于位置5(j+1)的元素。
这样就把区间更新通过差分的方式变成了在两个点处的更新,如果要求某个值,就可以直接对区间求和得到。
代码如下

/*对区间[i, j]同时加上x,只需要进行下面两步操作*/
add(i, x);
add(j+1, -x);

/*求第A[i]值*/
sum(i);

二维差分求某一点的值

俗话说只有更麻烦,没有最麻烦。

对于一个矩阵A,考虑下面两种操作:
1)改变从(x1, y1)(左上) 到 (x2, y2)(右下)的值,加上某个数x。
2)询问(x, y)处的值。

同样是区间更新,单点求值的问题。
对于二维的差分,由于多了一个维度,就稍微复杂一些。
首先根据一维差分,我们可以得出求A[i][j]值要通过以下方式
A[i][j] = ∑diff[x][y] (x的范围是0到i,y的范围是从0到j)

那么如何更新呢。
对于从(x1,y1)到(x2,y2)的部分,我们考虑通过下面的方式进行更新:
add[x1][y1][x2][y2] = add[x1][y1][x0][y0] - add[x1][y2+1][x0][y0] - add[x2+1][y1]x0][y0] + add[x2+1][y2+1][x0][y0]

高级数据结构——树状数组_第1张图片
转化成代码就是如下操作

add(x1, y1, val);
add(x1, y2+1, -val);
add(x2, y1, -val);
add(x2, y2+1, val);

可以做一下poj2155Matrix
可以把取反操作转变成加1,最后求结果的时候余2。

还有个区间更新,区间求和的操作,整个相对麻烦,需要用两个bit数组维护,这个之后在更新吧
poj3468A simple problem with integers
也可用线段树,我觉得比其树状数组还好理解点,只要加个lazy标记就行了。

你可能感兴趣的:(常用算法简单讲解,算法,数据结构,c++)