http://poj.org/problem?id=2155
题目大意:给定初始N*N矩阵,所有数都是0,然后有‘C’操作:对左上角(x1,y1)到右下角(x2,y2)的矩阵中所有数取非,即0变1,或1变0;还有‘Q’操作:查询点(x,y)的值并输出。
思路:构造一个线段树,每个结点还是线段树,外围线段树表示x方向,子线段树表示y方向。查询时对结点(x,y)所在的所有矩阵的值累计,这是因为在更新时,只更新到给定的矩阵(x1,x2,y1,y2),而对于这个矩阵中的点(x,y)则没有更新,而算法中没有懒操作,故查询时需将线段树中经过的每个矩阵都进行计算,以达到懒操作的功能。
代码如下:
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#include <cmath>
#include <string>
#include <climits>
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
#define N 1005
struct snode
{
int ly,ry;
bool num;
};
struct node
{
int lx,rx;
snode sst[4*N];
}st[4*N];
int ans;
void build_sub(snode sst[],int rt,int ly,int ry)
{
sst[rt].ly=ly;
sst[rt].ry=ry;
sst[rt].num=0;
if (ly==ry) return;
int mid=(ly+ry)>>1;
build_sub(sst,lson,ly,mid);
build_sub(sst,rson,mid+1,ry);
}
void build(int rt,int lx,int rx,int ly,int ry)
{
st[rt].lx=lx;
st[rt].rx=rx;
build_sub(st[rt].sst,1,ly,ry);
if (lx==rx) return;
int mid=(lx+rx)>>1;
build(lson,lx,mid,ly,ry);
build(rson,mid+1,rx,ly,ry);
}
void update_sub(snode sst[],int rt,int y1,int y2)
{
if (y1<=sst[rt].ly && sst[rt].ry<=y2)
{
sst[rt].num^=1;
return;
}
int mid=(sst[rt].ly+sst[rt].ry)>>1;
if (y1<=mid) update_sub(sst,lson,y1,y2);
if (y2>mid) update_sub(sst,rson,y1,y2);
}
void update(int rt,int x1,int y1,int x2,int y2)
{
if (x1<=st[rt].lx && st[rt].rx<=x2)
{
update_sub(st[rt].sst,1,y1,y2);
return;
}
int mid=(st[rt].lx+st[rt].rx)>>1;
if (x1<=mid) update(lson,x1,y1,x2,y2);
if (x2>mid) update(rson,x1,y1,x2,y2);
}
void query_sub(snode sst[],int rt,int y)
{
ans^=sst[rt].num;
if (y<=sst[rt].ly && sst[rt].ry<=y)
return ;
int mid=(sst[rt].ly+sst[rt].ry)>>1;
if (y<=mid) query_sub(sst,lson,y);
else query_sub(sst,rson,y);
}
void query(int rt,int x,int y)
{
query_sub(st[rt].sst,1,y);
if (x<=st[rt].lx && st[rt].rx<=x)
return;
int mid=(st[rt].lx+st[rt].rx)>>1;
if (x<=mid) query(lson,x,y);
else query(rson,x,y);
}
int main()
{
int t,n,m,x1,x2,y1,y2;
char ch[10];
while(scanf("%d",&t)!=EOF)
{
while (t--)
{
scanf("%d%d",&n,&m);
build(1,1,n,1,n);
//puts("built");
while (m--)
{
scanf("%s",ch);
if (ch[0]=='C')
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
update(1,x1,y1,x2,y2);
}
else
{
ans=0;
scanf("%d%d",&x1,&y1);
query(1,x1,y1);
printf("%d\n",ans);
}
}
printf("\n");
}
}
return 0;
}
另外有一种二维树状数组的实现方式,比较容易写,也容易理解些。
如果是在一维的树状数组,在[L,R]的区间内进行翻转操作后,我们把C[L]++;C[R+1]- -,这样每一个sum[x]记录的就是这个点所进行翻转的次数,次数为
奇数是该点值为1,次数为偶数时该点值为0。
同理可以扩展到二维的树状数组中
代码如下
#include <iostream>
#include <cstdio>
#include <string>
#include <memory.h>
using namespace std;
const int M = 1005;
int c[M][M];
int lowbit(int x)
{
return x & (-x);
}
void init();
void sol(int t);
void change(int x,int y,int d);
int sum(int x,int y);
int result(int x,int y,int a,int b);
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,ci;
scanf("%d%d",&c,&ci);
init();
sol(ci);
printf("\n");
}
return 0;
}
void init()
{
memset(c,0,sizeof(c));
return;
}
void sol(int t)
{
char op[5];
int x,y,a,b;
while(t--)
{
scanf("%s",&op);
if(op[0] == 'C')
{
scanf("%d%d%d%d",&x,&y,&a,&b);
int d = 1;
a++;b++;
change(x,y,d);
change(x,b,d);
change(a,y,d);
change(a,b,d);
}
else
{
x = y = 0;
scanf("%d%d",&a,&b);
printf("%d\n",result(x,y,a,b)&1);
}
}
return;
}
void change(int x,int y,int d)
{
int i,j;
for(i = x;i < M;i+=lowbit(i))
for(j = y;j < M;j+=lowbit(j))
c[i][j] += d;
}
int sum(int x,int y)
{
int i,j,r = 0;
for(i = x;i > 0;i -=lowbit(i))
for(j = y;j > 0;j -= lowbit(j))
r += c[i][j];
return r;
}
int result(int x,int y,int a,int b)
{
return sum(a,b) - sum(a,y) - sum(x,b) + sum(x,y);
}