bzoj4242水壶 BFS+最小生成树+倍增LCA
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj4242水壶 BFS+最小生成树+倍增LCA相关的知识,希望对你有一定的参考价值。
题目描述
输入
输出
样例输入
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的主要内容,如果未能解决你的问题,请参考以下文章
刷题总结——次小生成树(bzoj1977 最小生成树+倍增)
bzoj 3545/3551: [ONTAK2010]Peaks -- 主席树,最小生成树,倍增