LOJ #2876. 「JOISC 2014 Day2」水壶 题解

Description

JOI 君所居住的 IOI 市以一年四季都十分炎热著称。

IOI 市被分成 HH 行,每行包含 WW 块区域。每个区域都是建筑物、原野、墙壁之一。
IOI 市有 PP 个区域是建筑物,坐标分别为 (A1,B1),(A_1, B_1), (A2,B2),(A_2, B_2), ,\ldots, (AP,BP)(A_P, B_P)
JOI 君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外。

JOI 君因为各种各样的事情,必须在各个建筑物之间往返。虽然建筑物中的冷气设备非常好,但原野上太阳非常毒辣,因此在原野上每走过一个区域都需要 1 升水。此外,原野上没有诸如自动售货机、饮水处之类的东西,因此 IOI 市的市民一般都携带水壶出行。大小为 xx 的水壶最多可以装 xx 升水,建筑物里有自来水可以将水壶装满。
由于携带大水壶是一件很困难的事情,因此 JOI 君决定携带尽量小的水壶移动。因此,为了随时能在建筑物之间移动,请你帮他写一个程序来计算最少需要多大的水壶。

现在给出 IOI 市的地图和 QQ 个询问,第 ii 个询问包含两个整数 Si,S_i, TiT_i,对于每个询问,请输出:要从建筑物 SiS_i 移动到 TiT_i,至少需要多大的水壶?

数据范围:H,W2000H,W\leq2000P,Q2×105P,Q\leq2\times10^51Si,TiP1\leq S_i,T_i\leq P

Solution

首先对于任意两个建筑物连一条边权为他们的距离的边,那么对于每个询问,显然就是求最小瓶颈生成树两点间的最大权值。

考虑如何建出这个最小生成树。

可以先 bfs 求出距离每个原野最近的建筑物,那么对于每个建筑物,他管辖的原野一定是个连通块,考虑对于连通块有相邻边的两个建筑物连边,然后跑 kruskal 最小生成树。

感性理解一下,这样做显然能够连出一个生成树,并且对于每个点都保存了离他最近的几条边,所以这样做肯定能求出最小生成树。

时间复杂度:O(HW+(P+Q)logP)O\left(HW+(P+Q)\log P\right)

Code

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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxH = 2e3 + 5, kMaxP = 2e5 + 5, kMaxE = 4e6 + 5;
const int kDx[] = {0, 0, 1, -1}, kDy[] = {1, -1, 0, 0};

int h, w, p, q;
int a[kMaxP], b[kMaxP], fa[kMaxP][19], faw[kMaxP][19], dep[kMaxP], lg[kMaxP];
std::vector<std::pair<int, int>> G[kMaxP];
std::vector<std::tuple<int, int, int>> ed;
std::string s[kMaxH];

struct DSU {
std::vector<int> fa;

void init(int _n) { fa.resize(_n + 1), std::iota(fa.begin(), fa.end(), 0); }

DSU() {}
DSU(int _n) { init(_n); }

int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void unionn(int x, int y) {
int fx = find(x), fy = find(y);
if (fx != fy) fa[fx] = fy;
}
} dsu;

void bfs() {
static int dis[kMaxH][kMaxH], idx[kMaxH][kMaxH];
for (int i = 1; i <= h; ++i) {
for (int j = 1; j <= w; ++j) {
dis[i][j] = idx[i][j] = -1;
}
}
std::queue<std::tuple<int, int, int>> q;
for (int i = 1; i <= p; ++i) {
dis[a[i]][b[i]] = 0, idx[a[i]][b[i]] = i;
q.emplace(a[i], b[i], i);
}
for (; !q.empty();) {
auto [x, y, id] = q.front();
q.pop();
for (int k = 0; k < 4; ++k) {
int tx = x + kDx[k], ty = y + kDy[k];
if (tx < 1 || tx > h || ty < 1 || ty > w || s[tx][ty] == '#') continue;
if (!~idx[tx][ty]) {
dis[tx][ty] = dis[x][y] + 1, idx[tx][ty] = id;
q.emplace(tx, ty, id);
} else if (idx[tx][ty] != id) {
ed.emplace_back(dis[x][y] + dis[tx][ty], id, idx[tx][ty]);
}
}
}
}

void kruskal() {
dsu.init(p);
std::sort(ed.begin(), ed.end());
for (auto [w, u, v] : ed) {
if (dsu.find(u) != dsu.find(v)) {
dsu.unionn(u, v), G[u].emplace_back(v, w), G[v].emplace_back(u, w);
}
}
}

void dfs(int u, int fa) {
::fa[u][0] = fa, dep[u] = dep[fa] + 1;
for (int i = 1; i <= lg[p]; ++i) {
::fa[u][i] = ::fa[::fa[u][i - 1]][i - 1];
faw[u][i] = std::max(faw[u][i - 1], faw[::fa[u][i - 1]][i - 1]);
}
for (auto [v, w] : G[u]) {
if (v == fa) continue;
faw[v][0] = w;
dfs(v, u);
}
}

void lca_prework() {
lg[0] = -1;
for (int i = 1; i <= p; ++i)
lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= p; ++i) {
if (!dep[i]) {
dfs(i, 0);
}
}
}

int getans(int x, int y, int id = 0) {
if (dep[x] < dep[y]) std::swap(x, y);
int ret = 0;
for (int i = lg[p]; ~i; --i)
if (dep[fa[x][i]] >= dep[y])
ret = std::max(ret, faw[x][i]), x = fa[x][i];
if (x == y) return ret;
for (int i = lg[p]; ~i; --i) {
if (fa[x][i] != fa[y][i]) {
ret = std::max({ret, faw[x][i], faw[y][i]});
x = fa[x][i], y = fa[y][i];
}
}
return std::max({ret, faw[x][0], faw[y][0]});
}

void dickdreamer() {
std::cin >> h >> w >> p >> q;
for (int i = 1; i <= h; ++i) {
std::cin >> s[i];
s[i] = " " + s[i];
}
for (int i = 1; i <= p; ++i)
std::cin >> a[i] >> b[i];
bfs(), kruskal(), lca_prework();
for (int i = 1; i <= q; ++i) {
int s, t;
std::cin >> s >> t;
std::cout << (dsu.find(s) == dsu.find(t) ? getans(s, t, i) : -1) << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

LOJ #2876. 「JOISC 2014 Day2」水壶 题解
https://sobaliuziao.github.io/2024/02/14/post/1ba7d792.html
作者
Egg_laying_master
发布于
2024年2月14日
许可协议