链接:http://poj.org/problem?id=2155

题意:给一个矩阵,元素初始值都是0,对于一次修改操作,把要修改的矩阵中的0变为1,1变为0,给出要修改矩阵的左下角和右上角的位置。对于每个查询操作,输出矩阵某个位置上的值。

思路:二维的树状数组,有个学习的链接:http://www.java3z.com/cwbwebhome/article/article1/1369.html?id=4804

这道题要修改的是一个区间,求的是一个点,和最基本的树状数组不同。这里有个链接:http://hi.baidu.com/czyuan_acm/item/b14bff6ab6ffd093c5d249db 把树状数组的add()操作和sum()操作归一化了。挺好的。

由于要求最后的值,可以有两个方案,一是统计翻转次数,还有一个是进行模拟。我这里是统计的翻牌次数。如果某个点被翻牌的次数为偶数,那么最后值为0,被翻的次数为奇数,最后值为1.

先想一维的情况,要使区间(a,b)内的每个点的值都加d,那么区间(1,b)内点的值加d,区间(1,a-1)内点的值则要减d.

对于二维的情况,修改范围(x1,y1)到(x2,y2),那么(x2,y2)加d,(x1-1,y2)和(x2,y1-1)减d,(x1-1,y1-1)要加d.

由于这道题的翻牌的特性,如果某个区间我多翻了一次,那么我再翻一次就可以把上一次操作的影响抵消。所以就有了下面work()函数。

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=1000+5;
int n,C[maxn][maxn];

int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int y)
{
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=n;j+=lowbit(j))
            C[i][j]++;
}
void work(int x1,int y1,int x2,int y2)
{
    x1++;y1++;x2++;y2++;
    update(x2,y2);
    update(x1-1,y2);
    update(x2,y1-1);
    update(x1-1,y1-1);
}
int query(int x,int y)
{
    int s=0;
    for(int i=x;i>0;i-=lowbit(i))
        for(int j=y;j>0;j-=lowbit(j))
            s+=C[i][j];
    s=s%2;
    return s;
}

int main()
{
//    freopen("in.cpp","r",stdin);
    int t,q,x1,y1,x2,y2;
    char op[2];
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&q);
        memset(C,0,sizeof(C));
        while(q--)
        {
            scanf("%s",&op);
            if(op[0]=='C')
            {
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                work(x1,y1,x2,y2);
            }
            else
            {
                scanf("%d%d",&x1,&y1);
                printf("%d\n",query(x1,y1));
            }
        }
        printf("\n");
    }
    return 0;
}
View Code

 

 

 posted on 2013-09-04 20:42  ∑求和  阅读(318)  评论(0编辑  收藏  举报