我们已经学会了对于以为树状数组的常规操作,那么我们好奇(谁没事,这么的无聊)能不能把类似的操作放在矩阵上呢?这里我们就来写二维的树状数组。
我们了解了一维树状数组的原理,二维树状数组和一维树状数组类似,在二维树状数组中, 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=1x∑j=1y∑k=1i∑h=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=1x∑j=1yd[i][j]×(x+1−i)×(y+1−j)。
展开式为: ( 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=1x∑j=1yd[i][j]−(y+1)∑i=1x∑j=1yd[i][j]×i−(x+1)×∑i=1x∑j=1yd[i][j]×j+∑i=1x∑j=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;
}