1. 程式人生 > >【bzoj4242】水壺 BFS+最小生成樹+倍增LCA

【bzoj4242】水壺 BFS+最小生成樹+倍增LCA

強烈 scan 合並 names amp 字符 urn ont 貨車運輸

題目描述

JOI君所居住的IOI市以一年四季都十分炎熱著稱。 IOI市是一個被分成縱H*橫W塊區域的長方形,每個區域都是建築物、原野、墻壁之一。建築物的區域有P個,編號為1...P。 JOI君只能進入建築物與原野,而且每次只能走到相鄰的區域中,且不能移動到市外。 JOI君因為各種各樣的事情,必須在各個建築物之間往返。雖然建築物中的冷氣設備非常好,但原野上的日光十分強烈,因此在原野上每走過一個區域都需要1單位的水。此外,原野上沒有諸如自動售貨機、飲水處之類的東西,因此IOI市的市民一般都攜帶水壺出行。大小為x的水壺最多可以裝x單位的水,建築物裏有自來水可以將水壺裝滿。 由於攜帶大水壺是一件很困難的事情,因此JOI君決定攜帶盡量小的水壺移動。因此,為了隨時能在建築物之間移動,請你幫他寫一個程序來計算最少需要多大的水壺。 現在給出IOI市的地圖和Q個詢問,第i個詢問(1<=i<=Q)為“在建築物Si和Ti之間移動,最小需要多大的水壺?”,請你對於每個詢問輸出對應的答案。

輸入

第一行四個空格分隔的整數H,W,P,Q,表示IOI市被分成了縱H*橫W塊區域,有P個建築物,Q次詢問。 接下來H行,第i行(1<=i<=H)有一個長度為W的字符串,每個字符都是’.’或’#’之一,’.’表示這個位置是建築物或原野,’#’表示這個位置是墻壁。 接下來P行描述IOI市每個建築物的位置,第i行(1<=i<=P)有兩個空格分隔的整數Ai和Bi,表示第i個建築物的位置在第Ai行第Bi列。保證這個位置在地圖中是’.’ 接下來Q行,第i行(1<=i<=Q)有兩個空格分隔的整數Si和Ti,表示第i個詢問為“在建築物Si和Ti之間移動,最小需要多大的水壺?”

輸出

輸出Q行,第i行(1<=i<=Q)一個整數,表示在建築物Si和Ti之間移動最小需要多大的水壺。如果無法到達,輸出-1。此外,如果不需要經過原野就能到達,輸出0。

樣例輸入

5 5 4 4
.....
..##.
.#...
..#..
.....
1 1
4 2
3 3
2 5
1 2
2 4
1 3
3 4

樣例輸出

3
4
4
2


題解

BFS+最小生成樹+倍增LCA

一眼貨車運輸的既視感,然而需要先知道兩個點之間的距離。

好在這種網格圖是可以BFS的,所以把每個建築物壓到隊列中,跑BFS,記錄每個點被哪個點所搜到,以及與搜到點的距離是多少。

當搜到另一個被其它點搜到過的點時,將它們被搜到的建築物之間連邊,這樣邊集只有$O(4wh)$。然後跑Kruskal。

(這裏需要註意的是直接將這兩個點在並查集中合並是不行的,因為搜到的不一定是最短距離;而按照距離排序搜索也是不對的。必須把所有可能的邊連上跑最小生成樹才可行。)

這個過程中可以按照距離將所有的邊放到類似於桶的東西中,形成若幹個鏈表(或者使用vector),這樣能夠省去排序的過程。

然後get到最小生成樹之後跑倍增LCA即可。註意跑LCA時求的答案是路徑最大值,不是最小值。

另外,本題不僅僅是一棵樹,可能是森林,所以需要把每棵樹都遍歷一遍。別忘判斷-1的情況。

時間復雜度$O(wh+m\log n)$

#include <cstdio>
#include <algorithm>
#define K 2010
#define N 200010
using namespace std;
int first[K * K] , px[K * K * 2] , py[K * K * 2] , last[K * K * 2] , tot;
int map[K][K] , vis[K][K] , dis[K][K] , f[N] , qx[K * K] , qy[K * K] , l = 1 , r , head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , fa[19][N] , val[19][N] , deep[N] , log[N];
char str[K];
struct data
{
	int x , y;
	data() {}
	data(int x0 , int y0) {x = x0 , y = y0;}
}tmp[5];
bool cmp(data a , data b)
{
	return map[a.x][a.y] && map[b.x][b.y] ? vis[a.x][a.y] && vis[b.x][b.y] ? dis[a.x][a.y] < dis[b.x][b.y] : vis[a.x][a.y] : map[a.x][a.y];
}
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
void add(int x , int y , int z)
{
	to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
	to[++cnt] = x , len[cnt] = z , next[cnt] = head[y] , head[y] = cnt;
}
void ins(int t , int x , int y)
{
	px[++tot] = x , py[tot] = y , last[tot] = first[t] , first[t] = tot;
}
void drive(int x1 , int y1 , int x2 , int y2)
{
    if(!map[x2][y2]) return;
    if(!vis[x2][y2]) vis[x2][y2] = vis[x1][y1] , dis[x2][y2] = dis[x1][y1] + 1 , qx[++r] = x2 , qy[r] = y2;
    else if(vis[x1][y1] != vis[x2][y2]) ins(dis[x1][y1] + dis[x2][y2] , vis[x1][y1] , vis[x2][y2]);
}
void dfs(int x)
{
	int i;
	for(i = 1 ; i <= log[deep[x]] ; i ++ )
		fa[i][x] = fa[i - 1][fa[i - 1][x]] , val[i][x] = max(val[i - 1][x] , val[i - 1][fa[i - 1][x]]);
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa[0][x])
			fa[0][to[i]] = x , val[0][to[i]] = len[i] , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
}
int query(int x , int y)
{
	if(find(x) != find(y)) return -1;
	int i , ans = 0;
	if(deep[x] < deep[y]) swap(x , y);
	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
		if(deep[x] - deep[y] >= (1 << i))
			ans = max(ans , val[i][x]) , x = fa[i][x];
	for(i = log[deep[x]] ; ~i ; i -- )
		if(deep[x] >= (1 << i) && fa[i][x] != fa[i][y])
			ans = max(ans , max(val[i][x] , val[i][y])) , x = fa[i][x] , y = fa[i][y];
	if(x != y) ans = max(ans , max(val[0][x] , val[0][y]));
	return ans;
}
int main()
{
	int h , w , n , m , i , j , x , y;
	scanf("%d%d%d%d" , &h , &w , &n , &m);
	for(i = 1 ; i <= h ; i ++ )
	{
		scanf("%s" , str + 1);
		for(j = 1 ; j <= w ; j ++ ) map[i][j] = (str[j] == ‘.‘);
	}
	for(i = 1 ; i <= n ; i ++ )
		scanf("%d%d" , &qx[i] , &qy[i]) , vis[qx[i]][qy[i]] = f[i] = i , dis[qx[i]][qy[i]] = 0;
	for(r = n ; l <= r ; l ++ )
		x = qx[l] , y = qy[l] , drive(x , y , x + 1 , y) , drive(x , y , x - 1 , y) , drive(x , y , x , y + 1) , drive(x , y , x , y - 1);
	for(i = 0 ; i <= h * w ; i ++ )
	{
		for(j = first[i] ; j ; j = last[j])
		{
			x = px[j] , y = py[j];
			if(find(x) != find(y)) f[f[x]] = f[y] , add(x , y , i);
		}
	}
	for(i = 2 ; i <= n ; i ++ ) log[i] = log[i >> 1] + 1;
	for(i = 1 ; i <= n ; i ++ ) if(!fa[0][i]) dfs(i);
	while(m -- ) scanf("%d%d" , &x , &y) , printf("%d\n" , query(x , y));
	return 0;
}

【bzoj4242】水壺 BFS+最小生成樹+倍增LCA