一、二维前缀和(n+1)
1.求 pre[i][j](从 (1,1) 到 (i,j) 的矩形和)
|---|---|-----------|---------|---|
| | 1 | 2 | 3 | 4 |
| 1 | | | | |
| 2 | | | | |
| 3 | | (i-1,j-1) | (i-1,j) | |
| 4 | | (i,j-1) | (i,j) | |
| 5 | | | | |
preij = prei-1j + preij-1 - prei-1j-1 + aij;
2. 求任意子矩阵 (x1,y1) 到 (x2,y2) 的和
|-------|---|-------------|---------|---|-----------|
| | 1 | 2 | 3 | 4 | 5 |
| 1 | | | | | |
| 2 | | (x1-1,y1-1) | | | (x1-1,y2) |
| 3 | | | (x1,y1) | | |
| 4 | | | | | |
| 5 | | (x2,y1-1) | | | (x2,y2) |
| 6 | | | | | |
| 7 | | | | | |
sum = prex2y2 - prex1-1y2 - prex2y1-1 + prex1-1y1-1;
二、二维差分(n+2)
1. 核心关系
对 diff 做二维前缀和 = 原数组 a
也就是说:
aij = ai-1j + aij-1 - ai-1j-1 + diffij;
2. 怎么求 diff(从 a 构造)
diffij = aij - ai-1j - aij-1 + ai-1j-1;
和前缀和公式长得一样,但方向相反(减号变多了)。
3. 区间修改:让 (x1,y1) 到 (x2,y2) 每个数 + val
|------|---|---|-----------|---|---------|-------------|
| diff | 1 | 2 | 3 | 4 | 5 | n+2 |
| 1 | | | | | | |
| 2 | | | | | | |
| 3 | | | (x1,y1) | | | (x1,y2+1) |
| 4 | | | | | | |
| 5 | | | | | (x2,y2) | |
| n+2 | | | (x2+1,y1) | | | (x2+1,y2+1) |
改 4 个角:
diff[x1][y1] += val;
diff[x1][y2 + 1] -= val;
diff[x2 + 1][y1] -= val;
diff[x2 + 1][y2 + 1] += val;
记忆口诀:左上 +,右上 -,左下 -,右下 +
4. 完整流程
java
// 1. 从 a 构造 diff
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
diff[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1];
}
}
// 2. 多次区间修改(改 4 个角)
diff[x1][y1] += val;
diff[x1][y2 + 1] -= val;
diff[x2 + 1][y1] -= val;
diff[x2 + 1][y2 + 1] += val;
// 3. 对 diff 做二维前缀和,还原成 a
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + diff[i][j];
}
}