zoukankan      html  css  js  c++  java
  • POJ2155 简单二维线段树 区间翻转

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

      

  • 相关阅读:
    创建精灵--九宫格
    精灵灰化
    根据点中坐标,调整怪物动作方向
    从tableview中拖动某个精灵
    CCardSlip
    CCImage
    cocos2d-x---CCLabelTTF加载字体库
    CCAction、CCFiniteTimeAction、CCSpeed、CCFollow
    CCObject
    html中charset和content-language的区别
  • 原文地址:https://www.cnblogs.com/Lyush/p/2173505.html
Copyright © 2011-2022 走看看