链接: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; }
究竟是我抛弃了历史,还是历史遗弃了我。