平面范围求和
此类问题实际上是树状数组推广到二维的情形
平面子矩阵求和:POJ-2352
此题是求【0–x】【0–y】矩阵中除自身【x】【y】外的求和
- 二维解法:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stdio.h>
#define MAXSIZE 15005
using namespace std;
int val[MAXSIZE][MAXSIZE]; //二维树状数组
int main()
{
int lowbit(int x);
void update_2(int val[][MAXSIZE], int x, int y, int cal, int arry_num_x, int arry_num_y);
int sum_pre_2(int val[][MAXSIZE], int x, int y);
int power[15005]; //星能级数组
int num; //星数
int sum; //子区间和
int x, y;
while (cin >> num)
{
memset(val[0], 0, sizeof(int)*MAXSIZE*MAXSIZE);
memset(power, 0, sizeof(power));
for (int i = 1;i <= num;i++)
{
sum = 0;
cin >> x >> y;
update_2(val, x + 1, y + 1, 1, MAXSIZE,MAXSIZE); //坐标向右上方平移一位,防止数据中出现0坐标无法计算
sum += sum_pre_2(val, x + 1, y + 1) - 1; //计算去掉自身的子矩阵的和
power[sum]++;
}
for (int i = 0;i < num;i++)
cout << power[i] << endl;
}
return 0;
}
//二维树状数组
int lowbit(int x)
{//返回二进制数最低位的1对应的数值
return x & (-x); //与运算
}
void update_2(int val[][MAXSIZE], int x, int y, int cal, int arry_num_x,int arry_num_y)
{//当原数组A[x][y]+cal时,更新树状数组val,arry_num为行和列的最大长度
for (int i = x;i <= arry_num_x;i += lowbit(i))
for (int j = y;j <= arry_num_y;j += lowbit(j))
val[i][j] += cal;
}
int sum_pre_2(int val[][MAXSIZE], int x, int y)
{//求解A[x][y]左上方的子矩阵A[1--x][1--y]
int sum = 0;
for (int i = x;i > 0;i -= lowbit(i))
for (int j = y;j > 0;j -= lowbit(j))
sum += val[i][j];
return sum;
}
此代码弊端在于对内存空间占用极大虽可在编译器中通过,但对于此题内存超范围,无法通过。
- 一维解法:
因为输入数据x坐标已经是有序排列,所以只需要进行对y值的前缀数组求和即可(不包括自身)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stdio.h>
#define MAXSIZE 32005
using namespace std; //使用名称空间std
int val[MAXSIZE]; //树状数组
int power[15005]; //星能级数组
int main()
{
int lowbit(int x);
void update_1(int val[], int i, int cal, int arry_num);
int sum_pre_1(int val[], int i);
int num;
int x, y;
int sum;
while (cin >> num)
{
memset(val, 0, sizeof(val));
memset(power, 0, sizeof(power));
for (int i = 1;i <= num;i++)
{
sum = 0;
scanf("%d %d", &x, &y);
update_1(val, x + 1, 1, MAXSIZE);
sum += sum_pre_1(val, x + 1) - 1; //减去自己
power[sum]++;
}
for (int i = 0;i < num;i++)
printf("%d\n", power[i]);
}
return 0;
}
int lowbit(int x)
{//返回二进制数最低位的1对应的数值
return x & (-x); //与运算
}
void update_1(int val[], int i, int cal, int arry_num)
{//原数组第i个元素加上cal,更新树状数组相关元素,arry_num为原数组的长度
//可直接用于树状数组的建立
for (;i <= arry_num;i += lowbit(i))
val[i] += cal;
}
int sum_pre_1(int val[], int i)
{//求arry数组的前i项和
//val为树状数组地址
int sum = 0;
for (;i > 0;i -= lowbit(i)) //从后向前每次跳一个lowbit
sum += val[i];
return sum;
}
平面任意范围求和:POJ-1195
此题不过是子矩阵求和问题推广到矩阵任意范围矩阵求和
解法:子矩阵分割
#include <iostream>
#include <cstring>
#include <stdio.h>
#define MAXSIZE 1028
using namespace std; //使用名称空间std
int val[MAXSIZE][MAXSIZE];
int main()
{
int lowbit(int x);
void update_2(int val[][MAXSIZE], int x, int y, int cal, int arry_num_x, int arry_num_y);
int sum_pre_2(int val[][MAXSIZE], int x, int y);
int sum_between_2(int val[][MAXSIZE], int left, int right, int below, int top);
int flag;
int S, X, Y, A, L, B, R, T; //相关操作参数
while (~scanf("%d",&flag))
{
if (flag == 3)
break;
switch (flag)
{
case 0:
scanf("%d", &S);
memset(val[0], 0, sizeof(int)*S*S);
break;
case 1:
scanf("%d %d %d", &X, &Y, &A);
update_2(val, X + 1, Y + 1, A, S, S);
break;
case 2:
scanf("%d %d %d %d", &L, &B, &R, &T);
cout << sum_between_2(val, L + 1, R + 1, B + 1, T + 1) << endl;
break;
default:
break;
}
}
return 0;
}
//二维树状数组
int lowbit(int x)
{//返回二进制数最低位的1对应的数值
return x & (-x); //与运算
}
void update_2(int val[][MAXSIZE], int x, int y, int cal, int arry_num_x, int arry_num_y)
{//当原数组A[x][y]+cal时,更新树状数组val,arry_num为行和列的最大长度
for (int i = x;i <= arry_num_x;i += lowbit(i))
for (int j = y;j <= arry_num_y;j += lowbit(j))
val[i][j] += cal;
}
int sum_pre_2(int val[][MAXSIZE], int x, int y)
{//求A[x][y]左上方的子矩阵A[1--x][1--y]的和
int sum = 0;
for (int i = x;i > 0;i -= lowbit(i))
for (int j = y;j > 0;j -= lowbit(j))
sum += val[i][j];
return sum;
}
int sum_between_2(int val[][MAXSIZE], int left, int right, int below, int top)
{//求矩阵A[left--right][below--top]的和
return sum_pre_2(val, right, top) - sum_pre_2(val, right, below - 1) - sum_pre_2(val, left - 1, top) + sum_pre_2(val, left - 1, below - 1);
}