BZOJ-4242 水壶

题目大意

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

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

$H,Wleqslant 2000$,$P,Qleqslant 2times 10^5$。


思路

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

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

然后用Kruskal求出生成树,问题就变成了求树上两点间路径上的最大边权。预处理倍增数组$f[u][i]$,表示$u$号点到$fa[u][i]$的路径上的最大边权:

类似求LCA的过程,将其中某个点倍增向上跳的过程中,用跳过的这一段路径的最大边权更新答案。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=2000+5,maxp=2e5+5;
const int INF=0x3f3f3f3f;

template<typename Tp>
class graph{
public:
  struct edge{
    int to;edge *next;Tp len;
    edge(int to=0,edge *next=0,Tp len=0):to(to),next(next),len(len){}
  };
  int dep[maxp],fa[maxp][25],n,edgtot;
  edge edges[maxp*2],*now[maxp];
  Tp f[maxp][25];
  void insert(int u,int v,Tp w){
    edges[++edgtot]=edge(v,now[u],w),now[u]=edges+edgtot;
  }
  void dfs(int u){
    dep[u]=dep[fa[u][0]]+1;
    int k=ceil(log2(dep[u]));
    for(int i=1;i<=k;i++){
      fa[u][i]=fa[fa[u][i-1]][i-1];
      f[u][i]=max(f[u][i-1],f[fa[u][i-1]][i-1]);
    }
    for(edge *e=now[u];e;e=e->next){
      int v=e->to;
      if(v!=fa[u][0]){fa[v][0]=u,f[v][0]=e->len;dfs(v);}
    }
  }
  Tp lca(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    int s=ceil(log2(n)),k=dep[u]-dep[v];
    Tp ans=0;
    for(int i=0;i<=s;i++)if(k&1<<i)
      ans=max(ans,f[u][i]),u=fa[u][i];
    if(u==v)return ans;
    s=ceil(log2(dep[u]));
    for(int i=s;i>=0;i--)if(fa[u][i]!=fa[v][i]){
      ans=max(ans,max(f[u][i],f[v][i]));
      u=fa[u][i],v=fa[v][i];
    }
    return max(ans,max(f[u][0],f[v][0]));
  }
};
graph<int> g;

struct node{
  int x,y;
  node(int x=0,int y=0):x(x),y(y){}
};
struct edge{
  int a,b,len;
  edge(int a=0,int b=0,int len=0):a(a),b(b),len(len){}
  bool operator<(edge rhs)const{return len>rhs.len;}
};
const int dx[]={-1,1,0,0},dy[]={0,大专栏
上一篇:【洛谷P6329】【模板】点分树 | 震波


下一篇:luogu 4886