POJ-2155 简单二维线段树 区间翻转

Matrix
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 10544 Accepted: 3945

Description

Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1 <= i, j <= N). 

We can change the matrix in the following way. Given a rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2), we change all the elements in the rectangle by using "not" operation (if it is a '0' then change it into '1' otherwise change it into '0'). To maintain the information of the matrix, you are asked to write a program to receive and execute two kinds of instructions. 

1. C x1 y1 x2 y2 (1 <= x1 <= x2 <= n, 1 <= y1 <= y2 <= n) changes the matrix by using the rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2). 
2. Q x y (1 <= x, y <= n) querys A[x, y]. 

Input

The first line of the input is an integer X (X <= 10) representing the number of test cases. The following X blocks each represents a test case. 

The first line of each block contains two numbers N and T (2 <= N <= 1000, 1 <= T <= 50000) representing the size of the matrix and the number of the instructions. The following T lines each represents an instruction having the format "Q x y" or "C x1 y1 x2 y2", which has been described above. 

Output

For each querying output one line, which has an integer representing A[x, y]. 

There is a blank line between every two continuous test cases. 

Sample Input

1
2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1

Sample Output

1
0
0
1
 
  该题从操作上来讲是极像HDU敌兵布阵的,因为这里也是对点操作(指询问操作),在写一维线段树的时候,知道这时该种数据结构中最简单的操作,因为其未触及复杂的搜索更新操作,以及Lazy思想的处理。一直写的就是区间树,点树无视,所以虽然给定的是一个点,还是看作一条线段,只是区间为半开半闭而已。理由是对某条边而言, N 个点只有 N - 1个线段,所以要虚设一个点。
  该题是区间的翻转,之前遇到过染色问题,不小心将这两者混淆了,其实两者是有些区别的。
 
  染色(Lazy操作):给定一个区间就把这个区间的信息强制统一,其孩子节点在下一次直接对其操作前所含信息作废。(一个节点的属性由最后一次相关的操作决定)其性质在往下搜索的过程中由其上面的节点就可以代替了。因此每个节点就有个标记,标记该节点是否具有代表性,是否所保留的信息是作废的。如果某一次更新打破了某个节点的代表性,那么就会有以下操作:将该节点的标记取消,取消之前将信息传递给他的两个孩子,这个操作还没有改变这棵数的性质,并开启两个孩子的标记,然后再在两个孩子上进行操作,非常巧妙的操作。
 
  翻转:给定一个区间,将为 0 的区间设为 1,将为 1 的区间设置为 0 。(一个节点的属性由针对该区间操作的次数的奇偶性决定)与前者不同,如果给定一个更新的区间,同样的,遇到刚好满足左右边界的区间进行一次操作,这次操作的意义是 “该区间上的操作数加上 1 ”,此时,其孩子节点的信息并没作废,而是同样具有统计意义,也就是每次询问某个节点的时候是要一问到底的。与查看颜色不同,不会因中途得到信息而立即退出。
 
  二维线段树对应的操作均有两个,对应不同的维,每一个一维节点下有一棵线段数,在空间上的开销是比较大的。这也算是以空间换时间吧。写的时候犯了几个小的错误,给定的区间应该是和所给节点的左右边界去比。对于一个点而言,区间长度为一,只有要么在左树要么在右树两种情况。
  代码如下:
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define LSON p << 1
#define RSON p << 1 | 1
#define MID( x, y ) (x) + (y) >> 1
#define TWO( x, y ) (x) + (y) << 1
#define M( x, y ) memset( (x), (y), sizeof(x) )
using namespace std;

struct node
{
	int u, d, val;
}n[2605][2605];

struct Node
{
	int l, r;
	node *next;
}N[2605];

int M, Q, ans;

void y_build( node *r, int p, int u, int d )
{
	r[p].u = u, r[p].d = d, r[p].val = 0;
	if( d - u > 1 )
	{
		int m = MID( d, u );
		y_build( r, LSON, u, m );
		y_build( r, RSON, m, d );
	}
}

void build( int p, int l, int r )
{
	N[p].l = l, N[p].r = r, N[p].next = n[p];
	if( r - l > 1 )
	{
		int m = MID( l, r );
		build( LSON, l, m );
		build( RSON, m, r );
	}
	y_build( n[p], 1, 1, M + 1 );
}

void y_modify( node *r, int p, int u, int d )
{ 
	if( r[p].u == u && r[p].d == d )
	{
		r[p].val ^= 1;
		return;
	}
	int m = MID( r[p].u , r[p].d );
	if( m >= d )
		y_modify( r, LSON, u, d );
	else if( m <= u )
		y_modify( r, RSON, u, d );
	else
	{
		y_modify( r, LSON, u, m );
		y_modify( r, RSON, m, d );
	}
}

void modify( int p, int l, int r, int u, int d )
{
	if( N[p].l == l && N[p].r == r )
	{
		y_modify( N[p].next, 1, u, d );
		return;
	}
	int m = MID( N[p].l, N[p].r );
	if( m >= r )
		modify( LSON, l, r, u, d );
	else if( m <= l )
		modify( RSON, l, r, u, d );
	else
	{
		modify( LSON, l, m, u, d );
		modify( RSON, m, r, u, d );
	}
}
 
void y_cal( node *r, int p, int u, int d )
{
	if( r[p].u <= u && r[p].d >= d )
	{
		ans ^= r[p].val; 
		if( r[p].d - r[p].u == 1 )
			return;
	}
	int m = MID( r[p].u, r[p].d ); 
	if( m >= d )
		y_cal( r, LSON, u, d );
	else if( m <= u )
	{ 
		y_cal( r, RSON, u, d );
	}
}

void cal( int p, int l, int r, int u, int d )
{
	if( N[p].l <= l && N[p].r >= r )
	{
		y_cal( N[p].next, 1, u, d );
		if( N[p].r  - N[p].l == 1 )
			return;
	}
	int m = MID( N[p].l, N[p].r );
	if( m >= r )
		cal( LSON, l, r, u, d );
	else if( m <= l )
		cal( RSON, l, r, u, d );
}

int main()
{
	int T;
	scanf( "%d", &T );
	for( int t = 1; t <= T; ++t )
	{
		char op[5];
		int x1, x2, y1, y2;
		scanf( "%d %d", &M, &Q );
		build( 1, 1, M + 1 );
		for( int i = 0; i < Q; ++i )
		{
			scanf( "%s", op );
			if( op[0] == 'C' )
			{
				scanf( "%d %d %d %d", &x1, &y1, &x2, &y2 );
				modify( 1, x1, x2 + 1, y1, y2 + 1 );
			}
			else
			{
				ans = 0;
				scanf( "%d %d", &x1, &y1 );
				cal( 1, x1, x1 + 1, y1, y1 + 1 );
				printf( "%d\n", ans );
				
			}
		}
		if( t < T ) 
		    puts( "" );
	}
	return 0;
}

  

你可能感兴趣的:(poj)