【题目】BZOJ-4242 水壶

探讨了在特定地图上,利用最小生成树算法解决在建筑物间移动所需水壶最小容积的问题。通过BFS遍历和边的优化,有效减少了边的数量,使用Kruskal算法求解最小生成树,并通过预处理倍增数组求得树上两点间路径上的最大边权。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

某市是一个被分成H×WH\times WH×W块区域的长方形,每个区域都是建筑物、原野、墙壁之一。建筑物的区域有PPP个,编号为1…P1\dots P1P。只有建筑物和原野能够进入,而且每次只能走到相邻的区域中,且不能移动到市外。现在需要在各个建筑物之间往返。在原野上每走过一个区域都需要111单位的水。原野上不能提供水,但建筑物里有自来水可以将随身携带的水壶装满。

给出该市的地图和QQQ个询问,对于第iii个询问,输出在建筑物SiS_iSiTiT_iTi之间移动需要水壶的最小容积。

H,W⩽2000H,W\leqslant 2000H,W2000P,Q⩽2×105P,Q\leqslant 2\times 10^5P,Q2×105


思路

由于可以在建筑物中补充水,从SiS_iSi走到TiT_iTi可能会经过多个建筑物,则需要水壶的最小容积为连续经过的两个建筑物之间的距离的最大值。要使路径上的最大距离最小,可以想到最小生成树。

如果以每个建筑物为起点进行BFS,边的数量就达到O(P2)O(P^2)O(P2),不可取。可以先把PPP个建筑物全部放入队列中,同时向外扩展,用id[i]id[i]id[i]记下网格中区域iii最先被哪个建筑物扩展到,dist[i]dist[i]dist[i]记录扩展的距离。然后讨论相邻的两个区域i,ji,ji,j,将最先扩展到这两个区域的建筑物id[i],id[j]id[i],id[j]id[i],id[j]之间连边,边长为dist[i]+dist[j]dist[i]+dist[j]dist[i]+dist[j],这样边的数量就减少到了O(HW)O(HW)O(HW)

以下内容可能引起不适

少了这么多边,真的不会漏掉最小生成树上的边吗?
假设最小生成树上有一条边连接建筑物A,BA,BA,B

  • 如果图中有这条边,那么其长度一定是建筑物A,BA,BA,B间的最短距离。这个比较显然。

  • 如果图中没有这条边,先假设图中只有A,BA,BA,B两个建筑物,那么A,BA,BA,B路径上的区域一定可以从中间划分为两部分,一部分的ididid值为AAA,另一部分为BBB。那么讨论到路径的正中间的两个相邻区域x1,x2x_1,x_2x1,x2时,A,BA,BA,B之间就会连边。

    现在图中没有A,BA,BA,B这条边,说明x1,x2x_1,x_2x1,x2中至少有一个区域的ididid值发生了变化,原因是其它的建筑物先扩展到该区域。则A,BA,BA,B的路径上一定会有一段连续的被其它建筑物扩展到的区域[y1,y2][y_1,y_2][y1,y2],且两侧仍然会各有一个区域x1,x2x_1,x_2x1,x2AAABBB扩展到。

    根据ididid数组的定义,有

    dist(y1,id[y1])<dist(B,y1)dist(y2,id[y2])<dist(A,y2)\begin{aligned} dist(y_1,id[y_1])&<dist(B,y_1)\\ dist(y_2,id[y_2])&<dist(A,y_2) \end{aligned}dist(y1,id[y1])dist(y2,id[y2])<dist(B,y1)<dist(A,y2)

    dist(A,x1)+dist(y1,id[y1])<dist(A,x1)+dist(B,y1)dist(B,x2)+dist(y2,id[y2])<dist(A,y2)+dist(B,x2)\begin{aligned} dist(A,x_1)+dist(y_1,id[y_1])&<dist(A,x_1)+dist(B,y_1)\\ dist(B,x_2)+dist(y_2,id[y_2])&<dist(A,y_2)+dist(B,x_2) \end{aligned}dist(A,x1)+dist(y1,id[y1])dist(B,x2)+dist(y2,id[y2])<dist(A,x1)+dist(B,y1)<dist(A,y2)+dist(B,x2)

    dist(A,id[y1])<dist(A,B)dist(B,id[y2])<dist(A,B)\begin{aligned} dist(A,id[y_1])&<dist(A,B)\\ dist(B,id[y_2])&<dist(A,B) \end{aligned}dist(A,id[y1])dist(B,id[y2])<dist(A,B)<dist(A,B)

    同时有
    dist(y1,id[y1])<dist(A,y1)dist(y2,id[y2])<dist(B,y2)\begin{aligned} dist(y_1,id[y_1])&<dist(A,y_1)\\ dist(y_2,id[y_2])&<dist(B,y_2) \end{aligned}dist(y1,id[y1])dist(y2,id[y2])<dist(A,y1)<dist(B,y2)

    dist(y1,id[y1])+dist(y2,id[y2])<dist(A,y1)+dist(B,y2)dist(y_1,id[y_1])+dist(y_2,id[y_2])<dist(A,y_1)+dist(B,y_2)dist(y1,id[y1])+dist(y2,id[y2])<dist(A,y1)+dist(B,y2)

    dist(id[y1],id[y2])<dist(A,B)dist(id[y_1],id[y_2])<dist(A,B)dist(id[y1],id[y2])<dist(A,B)

    然后就发生了神奇的一幕:A,B,id[y2],id[y1]A,B,id[y_2],id[y_1]A,B,id[y2],id[y1]四个建筑物顺次构成了一个环,而这个环中连接A,BA,BA,B的边长度最长,所以这条边肯定不在最小生成树中。

回到正题

然后用Kruskal求出生成树,问题就变成了求树上两点间路径上的最大边权。预处理倍增数组f[u][i]f[u][i]f[u][i],表示uuu号点到fa[u][i]fa[u][i]fa[u][i]的路径上的最大边权,在求LCA的时候顺便统计答案。

另外本题求出的生成树可能是森林,所以每棵树都要DFS一遍求倍增数组,还要判断无解的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值