数据结构——二维树状数组

我们已经学会了对于以为树状数组的常规操作,那么我们好奇(谁没事,这么的无聊)能不能把类似的操作放在矩阵上呢?这里我们就来写二维的树状数组。

我们了解了一维树状数组的原理,二维树状数组和一维树状数组类似,在二维树状数组中, a r r [ x ] [ y ] arr[x][y] arr[x][y]记录的是右下角为 ​ ( x , y ) ​(x,y) (x,y),高度为 l o w b i t ( x ) lowbit(x) lowbit(x),宽度为 l o w b i t ( y ) lowbit(y) lowbit(y)的区间和。

单点修改+区间查询

void updata(int x, int y, int d){//将点(x, y)加上d
    int temp = y;
    for(; x <= n; x += lowbit(x)){//当x是固定值时,这就是一个横向的一维树状数组的操作
        for(y = temp; y <= n; y += lwbit(y)){//假设y是固定值时,这就是一个竖向的一维树状数组的操作
            arr[x][y] += d;
        }
    }
}
int getsum(int x, int y){//求左上角为(1,1)右下角为(x,y) 的矩阵和
    int res = 0, temp = y;
    for(;x > 0; x -= lowbit(x)){
        for(y = temp; y > 0; y -= lowbit(y)){
            res += arr[x][y];
        }
    }
    return res;
}

区间修改+单点查询

我们知道一维树状数组进行差分,就是先将原数组进行差分,在进行树状数组的操作,即一维差分+树状数组。同样的二维舒爽数组的区间修改+单点查询就是在二维差分的基础上进行树状数组操作。

void updata(int x, int y, int d){
	for(int i = x; i <= n; i += lowbit(i)){
		for(int j = y; j <= m; j += lowbit(j)){
			tree[i][j] += d;
		}
	}
}

ll getsum(int x, int y){
	ll res = 0;
	for(int i = x; i > 0; i -= lowbit(i)){
		for(int j = y; j > 0; j -= lowbit(j)){
			res += tree[i][j];
		}
	}
	return res;
}
void range_add(){
	cin >> a >> b >> c >> d >> k;
	updata(a,b,k);
	updata(a,d+1,-k);
	updata(c+1,b,-k);
	updata(c+1,d+1,k);
}

区间修改+区间查询

这个类似与前面一维区间修改+区间查询的问题,我们知道点 ( x , y ) (x,y) (x,y)的前缀和是: ∑ i = 1 x ∑ j = 1 y ∑ k = 1 i ∑ h = 1 j d [ k ] [ h ] \sum ^x _{i=1}\sum ^y _{j=1}\sum ^i _{k=1}\sum ^j _{h=1}d[k][h] i=1xj=1yk=1ih=1jd[k][h]。( d [ k ] [ h ] d[k][h] d[k][h]表示差分),这个式子我们可以写成: ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × ( x + 1 − i ) × ( y + 1 − j ) \sum ^x _{i=1}\sum ^y _{j=1}d[i][j]\times(x+1-i)\times(y+1-j) i=1xj=1yd[i][j]×(x+1i)×(y+1j)

展开式为: ( x + 1 ) × ( y + 1 ) × ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] − ( y + 1 ) ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × i − ( x + 1 ) × ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × j + ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] × i × j (x+1)\times (y+1)\times \sum ^x _{i=1}\sum ^y _{j=1}d[i][j]-(y+1)\sum ^x _{i=1}\sum ^y _{j=1}d[i][j]\times i-(x+1)\times\sum ^x _{i=1}\sum ^y _{j=1}d[i][j]\times j+\sum ^x _{i=1}\sum ^y _{j=1}d[i][j]\times i\times j (x+1)×(y+1)×i=1xj=1yd[i][j](y+1)i=1xj=1yd[i][j]×i(x+1)×i=1xj=1yd[i][j]×j+i=1xj=1yd[i][j]×i×j。所以我们只需要维护: d [ i ] [ j ] , d [ i ] [ j ] × x , d [ i ] [ j ] × y , d [ i ] [ j ] × x × y d[i][j],d[i][j]\times x,d[i][j]\times y,d[i][j]\times x\times y d[i][j],d[i][j]×x,d[i][j]×y,d[i][j]×x×y即可。

void updata(int x, int y, int d){
	for(int i = x; i <= n; i += lowbit(i)){
		for(int j = y; j <= m; j += lowbit(j)){
			tree1[i][j] += d;
			tree2[i][j] += x * d;
			tree3[i][j] += y * d;
			tree4[i][j] += x * y * d;
		}
	}
}

ll getsum(int x, int y){
	ll res = 0;
	for(int i = x; i > 0; i -= lowbit(i)){
		for(int j = y; j > 0; j -= lowbit(j)){
			res += (x + 1) * (y +1) * tree1[i][j] - (x + 1) * tree3[i][j] - (y + 1) * tree2[i][j] + tree4[i][j];
		}
	}
	return res;
}
void range_add(){
	cin >> a >> b >> c >> d >> k;
	updata(a,b,k);
	updata(a,d+1,-k);
	updata(c+1,b,-k);
	updata(c+1,d+1,k);
}
void print(){

	cin >> a >> b >> c >> d;
	cout << getsum(c,d) - getsum(c,b-1) - getsum(a-1,d) + getsum(a-1,b-1) << endl;
}

你可能感兴趣的:(数据结构)