给出一个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;
}
POJ 1195 Mobile phones(二维树状数组)
原文:http://www.cnblogs.com/From-scratch/p/7361850.html