题目大意:你有三种操作:
- S x1 y1 x2 y2 S x 1 y 1 x 2 y 2 表示查询从 (x1,y1) ( x 1 , y 1 ) 到 (x2,y2) ( x 2 , y 2 ) 之间这块矩形中所有数之和。
- A x1 y1 n1 A x 1 y 1 n 1 表示给 (x1,y1) ( x 1 , y 1 ) 那堆增加 n1 n 1 本书
- D x1 y1 n1 D x 1 y 1 n 1 表示给 (x1,y1) ( x 1 , y 1 ) 那堆减去 n1 n 1 本书(书目不足的话就全部拿走)
- M x1 y1 x2 y2 n1 M x 1 y 1 x 2 y 2 n 1 表示从 (x1,y1) ( x 1 , y 1 ) 这堆中拿出 n1 n 1 本书搬到 (x2,y2) ( x 2 , y 2 ) 上(书目不足全部搬走)
首先拿到题目,有很多修改操作,直接前缀和维护很难修改,二维线段树写起来又非常繁琐,现在可供我们使用的就是二维树状数组。
树状数组还不是很清楚的请戳这
树状数组的一大优点就是非常容易拓展到高维。我们先定义二维下的问题,类似地,大家可以自行定义更高维的情况。
定义一个二维数组
a[1..n,1..n]
a
[
1..
n
,
1..
n
]
,并维护以下两个操作:
- 修改:给 a[i,j] a [ i , j ] 加上一个增量 delta d e l t a
- 查询:询问左上角 a[1..x,1..y] a [ 1.. x , 1.. y ] 的和,即 ∑xi=1∑yj=1a[i,j] ∑ i = 1 x ∑ j = 1 y a [ i , j ] 。
我们同样用一个二维数组
sum
s
u
m
维护被分割的“子集”之和,模仿一维情形下的定义,将二维的
sum
s
u
m
数组定义如下:
sum[x,y]=∑xi=x−c(x)+1∑yj=y−c(y)+1a[i,j]
s
u
m
[
x
,
y
]
=
∑
i
=
x
−
c
(
x
)
+
1
x
∑
j
=
y
−
c
(
y
)
+
1
y
a
[
i
,
j
]
。
不妨做这样的类比:当
sum
s
u
m
数组的第一维坐标固定后,
sum
s
u
m
数组又可以看做以为的情形,只是这个一位数组是记录的二维数组
a
a
对应行的若干列合并之后的部分和。
看了代码之后应该就很清晰了。
int c[MAXSIZE][MAXSIZE];
namespace BIT_2D {
inline int lowbit(int x) {
return x & -x;
}
void update(int x, int y, int d) {
for (int i = x; i < MAXSIZE; i += lowbit(i))
for (int j = y; j < MAXSIZE; j += lowbit(j))
c[i][j] += d;
}
int query(int x, int y) {
int ret = 0;
for (int i = x; i > 0; i -= lowbit(i))
for (int j = y; j > 0; j -= lowbit(j))
ret += c[i][j];
return ret;
}
}
using namespace BIT_2D;
另外建议做了这道题之后去看看HDU3584,相信对你锻炼树状数组拓展到高维的能力会很有帮助。
下面来考虑怎么用二维树状数组实现上面几个操作。
- 操作,就是查询,我们先将两个数对调整为
x1<x2
x
1
<
x
2
和
y1<y2
y
1
<
y
2
, 便于进行查询,查询方法和二维前缀和完全一样。实际上就是容斥原理的应用。
int calc(int x1, int y1, int x2, int y2) { return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1); }
- A,D,M A , D , M 操作,单点修改,直接调用 update() u p d a t e ( ) 即可,若是减去就添个负号。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define MAXSIZE 1010 int c[MAXSIZE][MAXSIZE]; namespace BIT_2D { inline int lowbit(int x) { return x & -x; } void update(int x, int y, int d) { for (int i = x; i < MAXSIZE; i += lowbit(i)) for (int j = y; j < MAXSIZE; j += lowbit(j)) c[i][j] += d; } int query(int x, int y) { int ret = 0; for (int i = x; i > 0; i -= lowbit(i)) for (int j = y; j > 0; j -= lowbit(j)) ret += c[i][j]; return ret; } int calc(int x1, int y1, int x2, int y2) { return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1); } } using namespace BIT_2D; int main() { int T; scanf("%d", &T); for (int t = 1; t <= T; t++) { printf("Case %d:\n", t); memset(c, 0, sizeof c); for (int i = 1; i < MAXSIZE; i++) for (int j = 1; j < MAXSIZE; j++) update(i, j, 1); int m; scanf("%d", &m); for (int i = 1; i <= m; i++) { int x1, y1, x2, y2, n; char ch = getchar(); while (ch != 'S' && ch != 'A' && ch != 'D' && ch != 'M') ch = getchar(); if (ch == 'S') { scanf("%d%d%d%d", &x1, &y1, &x2, &y2); x1++; y1++; x2++; y2++; if (x1 > x2) std::swap(x1, x2); if (y1 > y2) std::swap(y1, y2); printf("%d\n", calc(x1, y1, x2, y2)); } else if (ch == 'A') { scanf("%d%d%d", &x1, &y1, &n); x1++; y1++; update(x1, y1, n); } else if (ch == 'D') { scanf("%d%d%d", &x1, &y1, &n); x1++; y1++; n = std::min(n, calc(x1, y1, x1, y1)); update(x1, y1, -n); } else if (ch == 'M') { scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &n); x1++; y1++; x2++; y2++; n = std::min(n, calc(x1, y1, x1, y1)); update(x1, y1, -n); update(x2, y2, n); } } } return 0; }