1. 程式人生 > >POJ 2155 Matrix(二維樹狀陣列+陣列陣列區間更新+單點查詢)

POJ 2155 Matrix(二維樹狀陣列+陣列陣列區間更新+單點查詢)

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,如果輸出字元為"Q"就是詢問矩陣x,y位置的值,為"C",然後輸出兩個點座標,表示矩陣的左上角和右下角座標,在這些矩陣裡面的值取反。。不知道用線段樹怎麼做,後來看一篇二維樹狀陣列的部落格看懂了。

首先寫出這題你要會樹狀陣列的區間更新,沒錯樹狀陣列也可以區間更新,原理不太清楚qaq我就先講一下做法,就是假如要在一個區間x,y上加上一個值v,那麼就在陣列sum[x]處加上v,在sum[y+1]處減去v,區間更新就完成了,不同的是當查詢單點處x的情況的時候,單點的值是sum[1]+...+sum[x],累加起來就是單點處的值了。

如何說說我的理解:

在sum[x]處加上v,因為更新是往後更新的(每次加上x&(-x)),所以這時後面的區間每個都多了個v,然後再sum[y+1]處減去v,就相當於把區間外多加上的v去掉,這就完成了[x,y]內加上v,因為這裡的區間更新比較特殊,所以查詢的時候每一點的值要把前面的區間值全部加上。。。感覺我說了我自己都不太懂,就這樣吧

然後因為講解這題要畫圖感受,所以我就直接貼我看過的部落格好了點選開啟連結

這個部落格的圖對這題的理解很有幫助,二維中,假如圖是n*n的,如果在[x,y]處加了v就相當於以左上角座標[x,y],到右下角座標[n*n]的大矩形裡面全部值加上了v,然後後續操作就是修剪加多了的地方,加上剪多了的地方最後就完成了更新

程式碼:

#include<iostream>
#include<stdio.h>
#include<map>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
using namespace std;
int sum[1005][1005];
int n;
int lowbit(int x)
{
    return x&(-x);
}
int getsum(int x,int y)
{
    int s=0;
    while(x>0)
    {
        int t=y;
        while(t>0)//二維需要理解
        {
            s+=sum[x][t];
            t-=lowbit(t);
        }
        x-=lowbit(x);
    }
    return s%2;//精髓
}
void update(int x,int y,int v)
{
    while(x<=n)//與查詢差不多
    {
        int t=y;
        while(t<=n)
        {
            sum[x][t]+=v;
            t+=lowbit(t);
        }
        x+=lowbit(x);
    }
}
int main()
{
    int i,j,test,m,x1,y1,x2,y2,x,y;
    char c;
    scanf("%d",&test);
    while(test--)
    {
        memset(sum,0,sizeof(sum));//要初始化
        scanf("%d%d",&n,&m);
        for(i=0;i<m;i++)
        {
            getchar();//吸收回車
            scanf("%c",&c);
            if(c=='C')
            {
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                update(x1,y1,1);//往大矩形里加上了1
                update(x2+1,y1,-1);
                update(x1,y2+1,-1);//修剪這個大矩形
                update(x2+1,y2+1,1);//加上多剪了的
            }
            else
            {
                scanf("%d%d",&x,&y);
                printf("%d\n",getsum(x,y));
            }
        }
        if(test)
            printf("\n");
    }
    return 0;
}