zoukankan      html  css  js  c++  java
  • 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
    ",(sum(x-1,y-1,x,y)&1));
                }
            }
            printf("
    ");
            
        }
        return 0;
    }


  • 相关阅读:
    【Excel】获取网页标题的VBA
    【IIS】windows2008 ii7 设置访问网站提示帐号密码登录
    【JS】JQUERY链接符大全
    【.NET】Repeater控件简单的数据绑定(有bool,日期,序号)
    CXF远程接口调用 用户名密码校验的方法:
    HTTP 请求/响应 设置/获取 Header参数
    zookeeper 实现分布式锁 demo(新)
    两个 中国标准时间 判断大小
    rabbitMq完整通信(三)---测试类
    rabbitMq完整通信(二)---consumer
  • 原文地址:https://www.cnblogs.com/ZefengYao/p/6602903.html
Copyright © 2011-2022 走看看