K - yousa的鸟蛋
题目大意
给定一个 n × n n \times n n×n的二维平面,有修改和查询两种操作:给平面上某个矩形里的每一个数都加上 k k k,查询某个矩形里面所有数的和。
题解
其实这题可以用很多种方法来做,树套树。
不过二维树状数组不失为一种简单的方法,因为相比线段树,树状数组的代码量更少。
- 运用差分的思想:
在一维树状数组中,能实现区间修改操作,就是运用了差分 的思想,于是在二维树状数组上面,为了能实现区间修改操作,同样也是要用的差分思想
记差分数组 b i , j = a i , j − a i − 1 , j − a i , j − 1 + a i − 1 , j − 1 b_{i,j} = a_{i,j} - a_{i - 1,j} - a_{i, j- 1} + a_{i -1 , j - 1} bi,j=ai,j−ai−1,j−ai,j−1+ai−1,j−1
那么,我们就可以得出下面的式子:
s i , j = ∑ x = 1 i ∑ y = 1 j a x , y = ∑ x = 1 i ∑ y = 1 j ∑ x x = 1 x ∑ y y = 1 y b x x , y y s_{i,j}=\sum _{x=1}^i \sum_{y=1}^j a_{x,y}=\sum _{x=1}^i \sum_{y=1}^j \sum _{xx=1}^x \sum _{yy=1}^y b_{xx,yy} si,j=x=1∑iy=1∑jax,y=x=1∑iy=1∑jxx=1∑xyy=1∑ybxx,yy
与一维树状数组类似,交换 ∑ \sum ∑的次序:
∑ x x = 1 i ∑ x = x x i ∑ y y = 1 j ∑ y = y y j b x x , y y \sum_{xx=1}^i \sum_{x=xx}^i \sum_{yy = 1} ^ j \sum _{y = yy} ^ j b_{xx ,yy} ∑xx=1i∑x=xxi∑yy=1j∑y=yyjbxx,yy
s i , j = ∑ x x = 1 i ∑ y y = 1 j ( i − x x + 1 ) × ( j − y y + 1 ) × b x x , y y s_{i,j}=\sum_{xx =1}^i \sum_{yy=1} ^j (i-xx+1)\times (j-yy+1) \times b_{xx,yy} si,j=∑xx=1i∑yy=1j(i−xx+1)×(j−yy+1)×bxx,yy
因此,我们只需要维护 ∑ b i , j \sum b_{i,j} ∑bi,j, ∑ b i , j \sum b_{i,j} ∑bi,j, ∑ b i , j \sum b_{i,j} ∑bi,j, ∑ b i , j \sum b_{i,j} ∑bi,j就可以达到维护前缀和的目的了。
时间复杂度
一维树状数组的时间复杂度是 O ( l o g n ) O(log_n) O(logn)的,那么二维的就是 O ( l o g n × l o g m ) O(log_n \times log_m) O(logn×logm)。
总的时间复杂度就是 O ( q × l o g n × l o g m ) O(q\times log_n \times log_m) O(q×logn×logm)
Tag
差分
二维树状数组
code
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define G getchar
#define nxt(x) (x & (- x))
using namespace std;
int read()
{
char ch;
for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
int n = 0 , w;
if (ch == '-')
{
w = -1;
ch = G();
} else w = 1;
for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
return n * w;
}
const int N = 1005;
long long s1[N][N] , s2[N][N] , s3[N][N] , s4[N][N] , ans;
int n , m , q , x , y , xx , yy , op , k , s[N][N];
void ins(int x , int y , int v )
{
long long vv = (long long) (x - 1) * v;
long long vvv = (long long) (y - 1) * v;
long long vvvv = (long long) (x - 1) * (y - 1) * v;
for (;x <= n ; x = x + nxt(x))
for (int j = y ; j <=m ; j = j + nxt(j))
{
s1[x][j] += v;
s2[x][j] += vv;
s3[x][j] += vvv;
s4[x][j] += vvvv;
}
}
void work(int x , int y , int xx , int yy , int v)
{
ins(x , y , v);
ins(xx + 1 , y , - v);
ins(x , yy + 1 , - v);
ins(xx + 1 , yy + 1 , v);
}
long long sum(int x , int y)
{
long long s = 0 ;
for (int i = x ; i ; i = i - nxt(i))
for (int j = y ; j ; j = j - nxt(j))
s += s1[i][j] * x * y - y * s2[i][j] - x * s3[i][j] + s4[i][j];
return s;
}
int main()
{
freopen("k.in","r",stdin);
//freopen("k.out","w",stdout);
n = read();
m = read();
q = read();
for (int i = 1; i <= n ; ++i)
for (int j = 1; j <= m ; ++j)
{
s[i][j] = read();
//work(i , j , i , j , s[i][j] - s[i - 1][j] - s[i][j - 1] + s[i - 1][j - 1]);
work(i , j , i , j , s[i][j]);
}
for (int i = 0; i < q; ++i)
{
op = read();
x = read();
y = read();
xx = read();
yy = read();
if (op == 1)
{
k = read();
work(x , y , xx , yy , k);
}
else
{
ans = sum(xx , yy) - sum(x - 1 , yy) - sum(xx , y -1) + sum(x - 1 , y - 1);
printf("%lld\n", ans);
}
}
}