POJ 1195 Mobile phones(二维树状数组,点修改,区间查询)

题目链接:POJ 1195 Mobile phones

 

【题目大意】




如图所示,开始的操作为 0  初始化 S * S大小的地图,值为0

操作 1, 输入 X Y A, 将地图中坐标为 (X,Y)的值修改为A

操作2, 输入 L B R T 查询 区间    (X,Y)   L<=X<=R , B<=Y<=T,  输出该矩形区间的和;

操作 3 结束程序


典型的二维树状数组

二维树状数组和一维树状数组原理是一样的。


我们先回顾一维树状数组 C【maxn】,

它的储存结构

C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
    
   
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
……
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
......

而二维树状数组的第二维 与普通一维树状数组原理是一样的

设二维树状数组为 C[][]


例:举个例子来看看C[][]的组成。

     设原始二维数组为:
  A[][]={{a11,a12,a13,a14,a15,a16,a17,a18,a19},
         {a21,a22,a23,a24,a25,a26,a27,a28,a29},
         {a31,a32,a33,a34,a35,a36,a37,a38,a39},
         {a41,a42,a43,a44,a45,a46,a47,a48,a49}};
那么它对应的二维树状数组C[][]呢?


B[1]={a11,a11+a12,a13,a11+a12+a13+a14,a15,a15+a16,...}  这是第一行的一维树状数组
B[2]={a21,a21+a22,a23,a21+a22+a23+a24,a25,a25+a26,...} 这是第二行的一维树状数组
B[3]={a31,a31+a32,a33,a31+a32+a33+a34,a35,a35+a36,...} 这是第三行的一维树状数组
B[4]={a41,a41+a42,a43,a41+a42+a43+a44,a45,a45+a46,...} 这是第四行的一维树状数组


那么:
C[1][1]=a11,C[1][2]=a11+a12,C[1][3]=a13,C[1][4]=a11+a12+a13+a14,c[1][5]=a15,C[1][6]=a15+a16,...
   这是A[][] 第一行 的一维树状数组

C[2][1]=a11+a21,C[2][2]=a11+a12+a21+a22,C[2][3]=a13+a23,C[2][4]=a11+a12+a13+a14+a21+a22+a23+a24,C[2][5]=a15+a25,C[2][6]=a15+a16+a25+a26,...
   这是A[][]数组 第一行第二行 相加后的树状数组

C[3][1]=a31,C[3][2]=a31+a32,C[3][3]=a33,C[3][4]=a31+a32+a33+a34,C[3][5]=a35,C[3][6]=a35+a36,...
   这是A[][] 第三行 的一维树状数组

C[4][1]=a11+a21+a31+a41,C[4][2]=a11+a12+a21+a22+a31+a32+a41+a42,C[4][3]=a13+a23+a33+a43,...
    这是A[][]数组 第一行+第二行+第三行+第四行 后的树状数组


注意加粗的部分,可以看出第二维之间储存值的关系和 第一维之间储存值得关系是一样的 ,

储存的项数都是 lowbit(i)。

所以修改和查询的函数,嵌套两个for循环就可以完成了

void modify(int a,int b,int val){
	for(int i=a;i<=n;i+=lowbit(i)){
		for(int j=b;j<=n;j+=lowbit(j))
			s[i][j]+=val;
	}
}
int sum(int x,int y){
	int ans=0;
	for(int i=x;i>0;i-=lowbit(i)){
		for(int j=y;j>0;j-=lowbit(j))
			ans+=s[i][j];
	}
	return ans;
}


以上数据来源: 树状数组讲的很好的文章


【源代码】

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 1111;
int s[maxn][maxn];
int n;
int lowbit(int x){ //求x二进制表示的最后一个1所在位置,,当然返回的值是最后一个1及其后面0所组成的二进制数
	return x&(-x);
}
void modify(int a,int b,int val){
	for(int i=a;i<=n;i+=lowbit(i)){
		for(int j=b;j<=n;j+=lowbit(j))
			s[i][j]+=val;
	}
}
int sum(int x,int y){
	int ans=0;
	for(int i=x;i>0;i-=lowbit(i)){
		for(int j=y;j>0;j-=lowbit(j))
			ans+=s[i][j];
	}
	return ans;
}
int getsum(int x1,int y1,int x2,int y2){
	return sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1); //先求最大区间 ,减去上面多余的,左边多余的, 加上左上角减去两次的。
}
int main(){
	int op;
	while(scanf("%d%d",&op,&n)!=EOF){
		memset(s,0,sizeof(s));
		int order;
		int a,b,c,d;
		while(scanf("%d",&order)!=EOF && order != 3){
			if(order==1){
				scanf("%d%d%d",&a,&b,&c);
				modify(a+1,b+1,c);
			}
			else {
				scanf("%d%d%d%d",&a,&b,&c,&d);
				int ans = getsum(a+1,b+1,c+1,d+1);
				printf("%d\n",ans);
			}
		}
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值