題目大意:你有三種操作:
- 表示查詢從 到 之間這塊矩形中所有數之和。
- 表示給 那堆增加 本書
- 表示給 那堆減去 本書(書目不足的話就全部拿走)
- 表示從 這堆中拿出 本書搬到 上(書目不足全部搬走)
首先拿到題目,有很多修改操作,直接前綴和維護很難修改,二維線段樹寫起來又非常繁瑣,現在可供我們使用的就是二維樹狀數組。
樹狀數組還不是很清楚的請戳這
樹狀數組的一大優點就是非常容易拓展到高維。我們先定義二維下的問題,類似地,大家可以自行定義更高維的情況。
定義一個二維數組 ,並維護以下兩個操作:
- 修改:給 加上一個增量
- 查詢:詢問左上角 的和,即 。
我們同樣用一個二維數組 維護被分割的“子集”之和,模仿一維情形下的定義,將二維的 數組定義如下: 。
不妨做這樣的類比:當 數組的第一維座標固定後, 數組又可以看做以爲的情形,只是這個一位數組是記錄的二維數組 對應行的若干列合併之後的部分和。
看了代碼之後應該就很清晰了。
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;
}