1. 程式人生 > >【POJ】1984 Navigation Nightmare 帶權並查集

【POJ】1984 Navigation Nightmare 帶權並查集

USACO 2004 February

傳送門:【POJ】1984 Navigation Nightmare

題目大意:
農夫約翰有 N 個農場,標號 1 到 N.M 條的不同的垂直或水平的道路連線著農場,道路的長度不超過 1000。這些農場的分佈就像下面的地圖一樣,圖中農場用 F1..F7 表示:
           F1 --- (13) ---- F6 --- (9) ----- F3

            |                                 |

           (3)                                |

            |                                (7)

           F4 --- (20) -------- F2            |

            |                                 |

           (2)                               F5

            | 

           F7 
每個農場最多能在東西南北四個方向連線 4 個不同的農場。此外,農場只處在道路的兩端。道路不會交叉且每對農場間有且僅有一條路徑。鄰居鮑勃要約翰來導航,但約翰丟了農場的地圖,他只得從電腦的備份中修復了。每一條道路的資訊如下:
從農場 23 往南經距離 10 到達農場 17
從農場 1 往東經距離 7 到達農場 17
……
當約翰重新獲得這些資料時,他有時被鮑勃的問題打斷:“農場 1 到農場 23 的曼哈頓距離是多少?”所謂在(x1,y1)和(x2,y2)之間的“曼哈頓距離”,就是|x1-x2|+|y1-y2|。如果已經有足夠的資訊,約翰就會回答這樣的問題 (在上例中答案是 17), 否則他會誠懇地抱歉並回答-1。

題目分析:經典的帶權並查集~
設所有的根結點的X,Y座標為0,然後每次更新時就更新每個點到根結點的相對座標即可。合併的時候根據兩個要合併的點到各自根結點的相對座標以及兩點之間的相對位移,可以得到一個根結點相對另一個根結點的座標。
設root[ x ]為x的根結點,X[ x ]為x到根結點的相對X座標,Y[ x ]為y到根結點的相對Y座標,dx為x到y的相對X位移,dy為x到y的相對Y位移。可得:
X[ root[ x ] ] = dx - X[ x ] + X[ y ] ;
Y[ root[ x ] ] = dy - Y[ x ] + 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 = 50000 ;
const int MAXE = 50000 ;

struct Edge {
	int x , y , n ;
	int idx ;
	void input ( int __idx ) {
		scanf ( "%d%d" , &x , &y ) ;
		idx = __idx ;
	}
} ;

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

Edge edge[MAXE] ;
Line line[MAXN] ;
int adj[MAXN] , cntE ;
int p[MAXN] ;
int X[MAXN] , Y[MAXN] ;
int ask[MAXN] ;
int n , m , t ;

int find ( int x ) {
	int tmp ;
	if ( p[x] == x )
		return x ;
	tmp = find ( p[x] ) ;
	X[x] += X[p[x]] ;
	Y[x] += Y[p[x]] ;
	return p[x] = tmp ;
}

void work () {
	int x , y , day , d ;
	while ( ~scanf ( "%d%d" , &n , &m ) ) {
		clear ( adj , -1 ) ;
		cntE = 0 ;
		REPF ( i , 1 , n )
			p[i] = i , X[i] = Y[i] = 0 ;
		REPF ( i , 1 , m )
			line[i].input () ;
		scanf ( "%d" , &t ) ;
		REP ( i , t ) {
			edge[cntE].input ( i ) ;
			scanf ( "%d" , &day ) ;
			edge[cntE].n = adj[day] ;
			adj[day] = cntE ++ ;
		}
		REPF ( i , 1 , m ) {
			x = find ( line[i].x ) ;
			y = find ( line[i].y ) ;
			d = line[i].d ;
			if ( x != y ) {
				p[x] = y ;
				if ( line[i].ch[0] == 'E' )
					X[x] = -X[line[i].x] + X[line[i].y] - d , Y[x] = -Y[line[i].x] + Y[line[i].y] ;
				else if ( line[i].ch[0] == 'W' )
					X[x] = -X[line[i].x] + X[line[i].y] + d , Y[x] = -Y[line[i].x] + Y[line[i].y] ;
				else if ( line[i].ch[0] == 'N' )
					Y[x] = -Y[line[i].x] + Y[line[i].y] - d , X[x] = -X[line[i].x] + X[line[i].y] ;
				else
					Y[x] = -Y[line[i].x] + Y[line[i].y] + d , X[x] = -X[line[i].x] + X[line[i].y] ;
			}
			for ( int j = adj[i] ; ~j ; j = edge[j].n ) {
				x = edge[j].x ;
				y = edge[j].y ;
				int flag = ( find ( x ) == find ( y ) ) ;
				ask[edge[j].idx] = ( flag ? abs ( X[x] - X[y] ) + abs ( Y[x] - Y[y] ) : -1 ) ;
			}
		}
		REP ( i , t )
			printf ( "%d\n" , ask[i] ) ;
	}
}

int main () {
	work () ;
	return 0 ;
}