poj2155 二维线段树

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);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值