您的位置:首页 > 其它

poj2155--Matrix--二维树状数组

2013-08-31 14:03 399 查看
Matrix

Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 15364 Accepted: 5775
Description

Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1 <= i, j <= N). 

We can change the matrix in the following way. Given a rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2), we change all the elements in the rectangle by using "not" operation (if it is a '0' then change it into '1' otherwise change
it into '0'). To maintain the information of the matrix, you are asked to write a program to receive and execute two kinds of instructions. 

1. C x1 y1 x2 y2 (1 <= x1 <= x2 <= n, 1 <= y1 <= y2 <= n) changes the matrix by using the rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2). 

2. Q x y (1 <= x, y <= n) querys A[x, y]. 

Input

The first line of the input is an integer X (X <= 10) representing the number of test cases. The following X blocks each represents a test case. 

The first line of each block contains two numbers N and T (2 <= N <= 1000, 1 <= T <= 50000) representing the size of the matrix and the number of the instructions. The following T lines each represents an instruction having the format "Q x y" or "C x1 y1 x2
y2", which has been described above. 

Output

For each querying output one line, which has an integer representing A[x, y]. 

There is a blank line between every two continuous test cases. 

Sample Input
1
2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1

Sample Output
1
0
0
1

Source

POJ Monthly,Lou Tiancheng

楼教出的题。。。在大家普遍觉得很水的情况下,我又小弱的理解查阅资料加问学长问了一天才搞懂。

首先这道题用线段树会MLE,但是据说开short就可以过。

但是这道题可以用二维树状数组过。

首先我们要知道树状数组可以干什么,

第一,树状数组可以完成点修改,区间查询。

第二,树状数组还可以完成区间修改,查询点。(但是一但这样做,就只能进行查询点了!不能再计算区间和)

第一点大家都知道,第二点的话,可以这样想象,

比如我要给【a,b】区间内所有点全部+T,那么我就在点a加T,再点b+1减T即可。

因为倘若我查询一个点X,(通过区间求和求点X的值)

分以下3种情况:1.x<a,则刚才的操作不影响点X的值

                              2.x在区间【a,b】之间,则点X的值加T

                              3.x>b,则刚才的操作也不影响点X的值,因为一个加T,一个减T,抵消了X的值的变化。

那么二维树状数组呢?以此类推。

比如给左下角(x1,y1)和右上角(x2,y2)两个点内的矩形中所有点全部加T。

即给(x1,y1)和(x2+1,y2+1)两个点加T,(x2+1,y1)和(x1,y2+1)两个点减T。

为什么这样做呢?

即保证!只有当一点在该矩形内时该点的值加T,不在该矩阵内时什么都不加也不减!

同时,

小弱的我原以为只有当所有点全部初始值为0的时候才能进行区间修改查询点,因为我每次进行进行的都是【1,X】的区间求和

而问了学长后发现,其实只要进行一次NlogN的初始操作即可。(N为加入点的个数)

即每加入一个点,由于其有初值,即在【a,a】区间内加上初值,再在a+1这个点减去初值即可!!!!!!!!!!!!!

以上。

而关于二维树状数组,即一个一维行向的树状数组,而在一维行向的树状数组每个元素上加上一个一维列向的树状数组即可

看这篇文章:

http://www.java3z.com/cwbwebhome/article/article1/1369.html?id=4804   

顺带给个二维树状数组的求和和修改点值的模板:

int getSum(int x,int y)//求和
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
for(int j=y;j>0;j-=lowbit(j))
sum+=matrix[i][j];
return sum;
}

void add(int x,int y,int adder)//点修改
{
for(int i=x;i<=row;i+=lowbit(i))
for(int j=y;j<=column;j+=lowbit(j))
matrix[i][j]+=adder;
}


以上。

以下是poj2155的代码:

//poj2155
#include <cstdio>
#include <cstring>
using namespace std;
int matrix[1010][1010];
int n;//matrix的实际大小
int lowbit(int t)
{
return t&(-t);
}

void add(int x,int y,int adder)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
matrix[i][j]+=adder;
}

int getSum(int x,int y)//看似区间求和实际上是查询一个点的值
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
for(int j=y;j>0;j-=lowbit(j))
sum+=matrix[i][j];
return sum;
}

int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
memset(matrix,0,sizeof(matrix));
int m;
scanf("%d%d",&n,&m);
while(m--)
{
getchar();
char c;
scanf("%c",&c);
if(c=='C')
{
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
add(x1,y1,1);
add(x2+1,y1,-1);
add(x1,y2+1,-1);
add(x2+1,y2+1,1);
}
else if(c=='Q')
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",getSum(x,y)&1);
}
}
if(T) printf("\n");//it's beautiful!
}
return 0;
}


由于只是0和1的区间翻转,也可以这样写

//poj2155
#include <cstdio>
#include <cstring>
using namespace std;
int matrix[1010][1010];
int n;//matrix的实际大小
int lowbit(int t)
{
return t&(-t);
}

void add(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=n;j+=lowbit(j))
matrix[i][j]^=1;
}

int getSum(int x,int y)//看似区间求和实际上是查询一个点的值
{
int sum=0;
for(int i=x;i>0;i-=lowbit(i))
for(int j=y;j>0;j-=lowbit(j))
sum^=matrix[i][j];
return sum;
}

int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
memset(matrix,0,sizeof(matrix));
int m;
scanf("%d%d",&n,&m);
while(m--)
{
getchar();
char c;
scanf("%c",&c);
if(c=='C')
{
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
add(x1,y1);
add(x2+1,y1);
add(x1,y2+1);
add(x2+1,y2+1);
}
else if(c=='Q')
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",getSum(x,y)&1);
}
}
if(T) printf("\n");//it's beautiful!
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  acm poj 线段树