poj 2155 Matrix

 

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].

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

题意:给定一个大小为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\n",(sum(x-1,y-1,x,y)&1));
            }
        }
        printf("\n");
        
    }
    return 0;
}

 



posted on 2017-03-22 23:47  ZefengYao  阅读(88)  评论(0编辑  收藏  举报

导航