树状数组相关应用之平面范围求和问题

平面范围求和

此类问题实际上是树状数组推广到二维的情形
平面子矩阵求和: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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值