poj2155 二维线段树

来源:互联网 发布:md5加密 java验证 编辑:程序博客网 时间:2024/04/24 15:07

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 1005struct 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);intd = 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);}


原创粉丝点击