Matrix
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 26668 | Accepted: 9830 |
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].
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.
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.
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
题意:给定一个大小为n*n的矩阵,初始时矩阵中每一个元素中的值都是0,现在进行T次操作,每一次操作如下:C:在这个矩阵中选定一个小的矩阵范围,对这个小范围中每一个值都进行反转,1变0,0变1。Q:给定编号,查询该编号中的值。
思路:《挑战程序设计竞赛》中讲述并推导了对一个线性区间同时加上一个值的做法,也就是同时维护两个一维的树状数组来记录数列的前i项的和。现在对于一个二维的区间范围内所有值同时加上一个值,同样可以通过维护四个二维的树状数组来
求出这个矩阵中前i行前j列范围内所有的值的和,对一个小的区间同时加上一个数v的具体思路如下:
设选定的小矩阵范围是(x1,x2]*(y1,y2],现在对该小区域的每一个元素都累加一个值v后,对于n*n矩阵中的每一个元素,若其在矩阵中的坐标我们记为(x,y),并且记该元素为Axy,那么∑1<=i<=x,1<=j<=yAij的值会变化多少呢,以Axy为自变量,我们再记函数ΔS(Axy)作为∑1<=i<=x,1<=j<=yAij的变化量,此时对于不同的Axy,相应的和值增量函数会有不同,分类讨论如下:对于每一个元素的(x,y)
(i):(x<x1+1&&y<y1+1)||(x<x1+1&&y>=y1+1)||(x>=x1+1&&y<y1+1)
ΔS(Axy)=0;//这三个区间不会受到影响
(ii):(x1+1<=x<=x2)&&(y1+1<=y<=y2)
ΔS(Axy)=(x-x1)(y-y1)v;
(iii):(x1+1<=x<=x2)&&y2<y<=n
ΔS(Axy)=(x-x1)(y2-y1)v;
(iiii):x2<x<=n&&(y1+1<=y<=y2)
ΔS(Axy)=(y-y1)(x2-x1)v;
(iiiii):n>=x>x2&&n>=y>y2
ΔS(Axy)=(x2-x1)(y2-y1)v;
可以得知,五大类情况和值的增量函数都可以表示成ΔS(Axy)=a*xy+b*x+c*y+d的形式(a,b,c,d均为常量),此时我们用四个2D的树状数组bit_xy,bit_x,bit_y,bit来分别累加a,b,c,d的值,即可方便的求出经过累加操作后的增量,
那么每一个元素的和值也可以轻松的进行更新操作。
那么该如何更新四个2D树状数组呢?
以bit_xy为例,并且以(i)->(ii)->(iii)->(iiii)->(iiiii)的顺序更新每一种情况中的值,
调整第一种情况,没有增量,bit_xy在该区域也不需要变化;
调整第二种情况,经过累加操作,该情况中每一个元素的和值的增量函数的xy项的系数都要加上v,那么我们对bit_xy在元素点(x1+1,y1+1)处加上v,这个操作的意义是:区间[x1+1,n]*[y1+1,n]上的所有元素点的和值的增量函数的xy项的系数
都加上了v,所以此时情况2的条件是满足了,但区间[x1+1,n]*[y1+1,n]上的其他情况增量函数的xy项的系数还要继续调整。
调整第三种情况,第三种情况增量函数的xy项的系数是0,并且在调整第二种情况的时候,也影响了第三种情况的区间,所以在第二种情况的基础上,bit_xy在元素点(x1+1,y2+1)处减去v,这就把第三种情况增量函数的xy项的系数从v又调回0了。
调整第四种情况,与调整情况三类似,在调整情况2的时候,对情况四的区间也进行的了调整,故bit_xy在元素点(x2+1,y1+1)处减去v;
调整第五种情况:可知调整前三种情况的时候,都对第五种情况的区间产生了影响,调整区间2的时候加了v,调整区间3,4时都减了v,而第五种情况增量函数的xy项的系数是0,那么此时对bit_xy在点(x2+1,y2+1)要加上v才能抵消前三次调整产生
的影响。
其余的三个树状数组的调整方法类似,只是调整区间的顺序必须保持一致。
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int N_MAX = 1000+2; int N, T; char TT; struct BIT2D { ll data[N_MAX][N_MAX]; int size; void init(int size) { memset(data,0,sizeof(data)); this->size = size; } ll sum(int x,int y) {//从(0,0)到(x,y)点为止的矩阵的和 ll res = 0; for (int dy = y; dy; dy -= dy&-dy) { for (int dx = x; dx; dx -= dx&-dx) { res+=data[dy][dx]; } } return res; } void add(int x,int y,ll v) { for (int dy = y + 1; dy <= size; dy += dy&-dy) {//!1!! for (int dx = x + 1; dx <= size; dx += dx&-dx) {//+1是为了化简后面的操作 data[dy][dx] += v; } } } }; BIT2D bit_xy, bit_x, bit_y, bit; //对区间(x1,x2]*(y1,y2]的部分都加上v void add(int x1,int y1,int x2,int y2,ll v) { bit_xy.add(x1,y1,v); bit_xy.add(x1,y2,-v); bit_xy.add(x2,y1,-v); bit_xy.add(x2,y2,v); bit_x.add(x1, y1, -y1*v); bit_x.add(x1, y2, v*y2); bit_x.add(x2,y1,y1*v); bit_x.add(x2,y2,-v*y2); bit_y.add(x1,y1,-x1*v); bit_y.add(x1,y2,x1*v); bit_y.add(x2,y1,x2*v); bit_y.add(x2, y2, -x2*v); bit.add(x1,y1,x1*y1*v); bit.add(x1,y2,-x1*y2*v);/////打错数字!!!! bit.add(x2,y1,-x2*y1*v); bit.add(x2,y2,x2*y2*v); } ll sum(int x,int y) { return bit_xy.sum(x, y)*x*y + bit_x.sum(x, y)*x + bit_y.sum(x, y)*y + bit.sum(x, y); } ll sum(int x1,int y1,int x2,int y2) {//计算区间左开右闭 return sum(x1, y1) + sum(x2, y2) - sum(x1, y2) - sum(x2, y1);//!!!打错数字!!!! } int main() { int X; scanf("%d",&X); while (X--) { scanf("%d%d",&N,&T); bit.init(N); bit_x.init(N); bit_y.init(N); bit_xy.init(N); for (int i = 0; i < T;i++) { scanf(" %c",&TT); if (TT == 'C') { int x1, y1, x2, y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); add(x1-1,y1-1,x2,y2,1); } else { int x, y; scanf("%d%d",&x,&y); printf("%lld ",(sum(x-1,y-1,x,y)&1)); } } printf(" "); } return 0; }