题目链接:POJ 1195
题意:
给出一个S*S的矩阵(行、列号从1开始),每个元素初始值为0,有两种操作:一种是第X行第Y列元素值加A;另一种是查询给定范围矩阵的所有元素之和(L<=X<=R,B<=Y<=T)。
分析:
查询给定范围矩阵的所有元素之和是二维区间和,可以转换为二维前缀和求值。类比一维前缀和求法,二维区间和S(L, B, R, T) = S(1, 1, R, T) - S(1 ,1, L-1, T) - S(1, 1, R, B-1) + S(1, 1, L-1, B-1)。单点更新一个元素的值,修改二维前缀和,类比一维树状数组的更新操作,二维树状数组的更新操作(参考代码)。
总结:
二维树状数组可以理解为:先固定X=i,把Y=1~S看作一维树状数组就比较容易理解,再X=1~S看作一维树状数组,这样组合成二维树状数组。如果还想不明白,可以想想二元积分是怎么做的。
代码实现:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <cmath> using namespace std; const int N = 1024 + 5; int C[N][N], cmd, s, x, y, a, l, b, r, t; int lowbit(int x) { return x & (-x); } void add(int x, int y, int a) { for (int i = x; i <= s; i += lowbit(i)) for (int j = y; j <= s; j += lowbit(j)) C[i][j] += a; } int sum(int x, int y) { int ret = 0; for (int i = x; i; i -= lowbit(i)) for (int j = y; j; j -= lowbit(j)) ret += C[i][j]; return ret; } int main() { while (~scanf("%d", &cmd)) { if (cmd == 3) break; if (cmd == 0) { scanf("%d", &s); for (int i = 1; i <= s; i++) for (int j = 1; j <= s; j++) C[i][j] = 0; continue; } if (cmd == 1) { scanf("%d%d%d", &x, &y, &a); x++, y++; add(x, y, a); continue; } if (cmd == 2) { scanf("%d%d%d%d", &l, &b, &r, &t); l++, b++, r++, t++; int ans = sum(r, t) - sum(l-1, t) - sum(r, b-1) + sum(l-1, b-1); printf("%d\n", ans); } } return 0; }
时间: 2024-10-03 13:46:19