题目大意:你有三种操作:
- 表示查询从 到 之间这块矩形中所有数之和。
- 表示给 那堆增加 本书
- 表示给 那堆减去 本书(书目不足的话就全部拿走)
- 表示从 这堆中拿出 本书搬到 上(书目不足全部搬走)
首先拿到题目,有很多修改操作,直接前缀和维护很难修改,二维线段树写起来又非常繁琐,现在可供我们使用的就是二维树状数组。
树状数组还不是很清楚的请戳这
树状数组的一大优点就是非常容易拓展到高维。我们先定义二维下的问题,类似地,大家可以自行定义更高维的情况。
定义一个二维数组
,并维护以下两个操作:
- 修改:给 加上一个增量
- 查询:询问左上角 的和,即 。
我们同样用一个二维数组
维护被分割的“子集”之和,模仿一维情形下的定义,将二维的
数组定义如下:
。
不妨做这样的类比:当
数组的第一维坐标固定后,
数组又可以看做以为的情形,只是这个一位数组是记录的二维数组
对应行的若干列合并之后的部分和。
看了代码之后应该就很清晰了。
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,相信对你锻炼树状数组拓展到高维的能力会很有帮助。
下面来考虑怎么用二维树状数组实现上面几个操作。
- 操作,就是查询,我们先将两个数对调整为 和 , 便于进行查询,查询方法和二维前缀和完全一样。实际上就是容斥原理的应用。
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);
}
- 操作,单点修改,直接调用 即可,若是减去就添个负号。
#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;
}