• 【bzoj4242】水壶 BFS+最小生成树+倍增LCA


    题目描述

    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+mlog 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
    " , query(x , y));
    	return 0;
    }
    

     

  • 相关阅读:
    php ReflectionObject类
    is_callable
    Zend Framework: Accessing Request, Response & Router Object from anywhere
    (转)zend异常处理
    call_user_func
    zend framework基础教程6—视图
    php func_get_arg
    zend framework基础教程3—创建index.php
    【算法02】3种方法求解斐波那契数列
    【算法01】寻找丑数
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7123525.html
Copyright © 2020-2023  润新知