【POJ】1733 Parity game 并查集

传送门:【POJ】1733 Parity game


题目大意:给你一个长度为n的01序列,再给你m句话,每句话是一个区间【L,R】,告诉你区间【L,R】中1的个数,现在你的任务是找到从第几句话开始说的和前面矛盾,出现第一次假话的时候前面有多少是真话。


题目分析:一开始看几乎没思路啊。后来没办法了,只能跑别人的博客去看看了。。。一看到说把一个区间【L,R】拆成两个区间【0,L-1】,【0,R】。。然后马上关了别人的博客继续自己想了。。。。后来过了几天才明白,话说拆开来思考不失为一种不错的思路。

如果把区间转化了我们可以得到什么不错的性质呢?假设【L,R】区间内1的个数为偶数个e,那么设【0,L-1】内1的个数为x个,那么x+e与e一定是同奇同偶的(很显然吧。。),说明【0,L-1】与【0,R】内1的个数都是一个属性的(奇数个或偶数个)。当然如果【L,R】内1的个数为奇数个,那么【0,L-1】与【0,R】内1的个数都是不同属性的了。根据这一信息,我们可以构造并查集。

我们用r[ ]数组保存当前节点和根结点的关系(1表示不同,0表示相同,根结点的r[ ]始终为0),然后按照带权并查集的做法做好了,这里我将右端点R接到左端点L-1上(表示区间【0,R】和【0,L-1】的关系是已经确定了的),然后就可以愉快的真假并查集了。

真假并查集也就是说,用0~1表示当前节点与根结点的关系,如果等于0(或1)说明与根结点关系相同,如果等于1(或0)说明与根结点相同。如果访问的两个节点在同一个集合,也就是说他们之间的关系通过他们和根结点的关系可以间接的推出来了,如果他们的关系和描述相符,没事,否则就说明这个描述是和之前的话矛盾的!当然如果访问的两个节点属于不同的集合,那么根据这两个节点的关系确定其中一个根相对于另一个根的偏移量然后再接到另一个根身上就好了,并查集的时候顺便更新途径的点和新根结点的关系就行了。

偏移量可以这么求得:如果X的根是x,相对于x的偏移量为d1,Y的根是y,相对于y的偏移量d2,X相对于Y的偏移量为d,如果x接到y身上,那么x相对于y的偏移量r = -d1(-d1为x相对与X的偏移量)+d(+d为X相对于Y的偏移量)+d2(+d2为Y相对于y的偏移量)。当然需要取模得到适当的偏移量表示关系。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clear( a , x ) memset ( a , x , sizeof a )

const int MAXN = 10005 ;

struct Node {
	int x , y ;
	char s[5] ;
	void input () {
		scanf ( "%d%d%s" , &x , &y , s ) ;
	}
} ;

Node A[MAXN] ;
int a[MAXN] ;
int r[MAXN] ;
int p[MAXN] ;
int n ;

int find ( int x ) {
	if ( p[x] == x )
		return x ;
	int ans = find ( p[x] ) ;
	r[x] ^= r[p[x]] ;
	return p[x] = ans ;
}

int unique ( int a[] , int n ) {
	int cnt = 1 ;
	sort ( a + 1 , a + n + 1 ) ;
	REPF ( i , 2 , n )
		if ( a[i] != a[cnt] )
			a[++ cnt] = a[i] ;
	return cnt ;
}

int binary_search ( int x , int l , int r ) {
	while ( l < r ) {
		int m = ( l + r ) >> 1 ;
		if ( a[m] >= x )
			r = m ;
		else
			l = m + 1 ;
	}
	return l ;
}

void solve () {
	int cnt = 0 ;
	REP ( i , n ) {
		A[i].input () ;
		a[++ cnt] = A[i].x - 1 ;
		a[++ cnt] = A[i].y ;
	}
	cnt = unique ( a , cnt ) ;
	REPF ( i , 0 , cnt )
		p[i] = i ;
	REP ( i , n ) {
		int L = binary_search ( A[i].x - 1 , 1 , cnt + 1 ) ;
		int R = binary_search ( A[i].y , 1 , cnt + 1 ) ;
		int x = find ( L ) ;
		int y = find ( R ) ;
		int ch = A[i].s[0] == 'o' ? 1 : 0 ;
		if ( x == y && ( r[L] ^ r[R] ) != ch ) {
			printf ( "%d\n" , i ) ;
			return ;
		}
		else if ( x != y ) {
			p[y] = x ;
			r[y] = r[L] ^ r[R] ^ ch ;
		}
	}
	printf ( "%d\n" , n ) ;
}

int main () {
	while ( ~scanf ( "%*d%d" , &n ) )
		solve () ;
	return 0 ;
}


你可能感兴趣的:(poj)