POJ 2155 Matrix (二位树状数组)

14 篇文章 1 订阅
11 篇文章 1 订阅



G - Matrix
Time Limit:3000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu
Appoint description: 

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


如果还不会二位树状数组,这里有一篇博客讲树状数组所有基本应用都讲得很详细很好,可以去学一下:http://blog.csdn.net/qq_34374664/article/details/52787481

题意:每次操作可以是编辑某个矩形区域,这个区域的0改为1,1改为0,每次查询只查询某一个点的值是0还是1.

思路:典型的二位树状数组,用以前的区间更新单点查询比较好理解。。。一开始更新x1,y1,这样x1-n,y1-n都给+1了,所以要将x1, y2+1, 减一,x1+1,y2减一 这样之后x2 y2 就多减了一次1所以要再加一次1、

由于是0-1矩阵,只需用tre[][]记录一个元素被置反的次数即可,当对(x1,y1),(x2,y2)区间置反时,需要改动四个地方就是4个角就可以了。为什么呢?如下图,假设A区未需要置反的区域,因为改动A区的左上角时,由树状数组的性质知:A,B,C,D4个区域都是要被置反的,所以在依次置反BD,CD,D,这样,置反的总过程为ABCD,BD,CD,D,这样我们就会发现结果对2取模时,只有A区被置反,B,C,D三个区都没有变化。明白原理之后就好做了。

A

B

C

D



树状数组代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1e3 + 5;
int c[maxn][maxn], t, n, m;
void update(int x, int y, int val)
{
    while(x <= n)
    {
        int y1 = y;
        while(y1 <= n)
        {
            c[x][y1] += val;
            y1 += y1 & -y1;
        }
        x += x & -x;
    }
}
int sum(int x, int y)
{
    int s = 0;
    while(x > 0)
    {
        int y1 = y;
        while(y1 > 0)
        {
            s += c[x][y1];
            y1 -= y1 & -y1;
        }
        x -= x & -x;
    }
    return s;
}
int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        memset(c, 0, sizeof(c));
        char ch;
        while(m--)
        {
            int x1, x2, y1, y2;
            scanf(" %c", &ch);
            if(ch == 'C')
            {
                scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
                update(x1, y1, 1);   //按照题解的思路 这里都可以是+1的,因为多翻转一次等于没有翻转
                update(x1, y2+1, -1);
                update(x2+1, y1, -1);
                update(x2+1, y2+1, 1);
            }
            if(ch == 'Q')
            {
                scanf("%d%d", &x1, &y1);
                printf("%d\n", sum(x1,y1)%2);
            }
        }
        printf("\n");
    }
    return 0;
}

kuangbin线段树代码

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
const int MAXN = 1010;
struct Nodey
{
    int l,r;
    int val;
};
int n;
int locx[MAXN],locy[MAXN];
struct Nodex
{
    int l,r;
    Nodey sty[MAXN*3];
    void build(int i,int _l,int _r)
    {
        sty[i].l = _l;
        sty[i].r = _r;
        sty[i].val = 0;
        if(_l == _r)
        {
            locy[_l] = i;
            return;
        }
        int mid = (_l + _r)>>1;
        build(i<<1,_l,mid);
        build((i<<1)|1,mid+1,_r);
    }
    void add(int i,int _l,int _r,int val)
    {
        if(sty[i].l == _l && sty[i].r == _r)
        {
            sty[i].val += val;
            return;
        }
        int mid = (sty[i].l + sty[i].r)>>1;
        if(_r <= mid)add(i<<1,_l,_r,val);
        else if(_l > mid)add((i<<1)|1,_l,_r,val);
        else
        {
            add(i<<1,_l,mid,val);
            add((i<<1)|1,mid+1,_r,val);
        }
    }
}stx[MAXN*3];
void build(int i,int l,int r)
{
    stx[i].l = l;
    stx[i].r = r;
    stx[i].build(1,1,n);
    if(l == r)
    {
        locx[l] = i;
        return;
    }
    int mid = (l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
}
void add(int i,int x1,int x2,int y1,int y2,int val)
{
    if(stx[i].l == x1 && stx[i].r == x2)
    {
        stx[i].add(1,y1,y2,val);
        return;
    }
    int mid = (stx[i].l + stx[i].r)/2;
    if(x2 <= mid)add(i<<1,x1,x2,y1,y2,val);
    else if(x1 > mid)add((i<<1)|1,x1,x2,y1,y2,val);
    else
    {
        add(i<<1,x1,mid,y1,y2,val);
        add((i<<1)|1,mid+1,x2,y1,y2,val);
    }
}
int sum(int x,int y)
{
    int ret = 0;
    for(int i = locx[x];i;i >>= 1)
        for(int j = locy[y];j;j >>= 1)
            ret += stx[i].sty[j].val;
    return ret;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int q;
        scanf("%d%d",&n,&q);
        build(1,1,n);
        char op[10];
        int x1,x2,y1,y2;
        while(q--)
        {
            scanf("%s",op);
            if(op[0] == 'C')
            {
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                add(1,x1,x2,y1,y2,1);
            }
            else
            {
                scanf("%d%d",&x1,&y1);
                if(sum(x1,y1)%2 == 0)printf("0\n");
                else printf("1\n");
            }
        }
        if(T)printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值