Luogu P3684 [CERC2016]机棚障碍 Hangar Hurdles

题目描述

https://www.luogu.com.cn/problem/P3684

简要题意:给定一个 $n\times n$ 四连通网格图,有一些位置有障碍,现在有 $m$ 次询问,每次询问给定起点 $(x_1,y_1)$ 和终点 $(x_2, y_2)$,求一个最大的 $k$,满足存在一个从起点到终点的路径,使得路径上的每个点到离它最近的障碍点切比雪夫距离小于 $k$

$n\le 1000,m\le 3\times 10^5$

Solution

我们定义每个非障碍点的点权为这个点距离最近的障碍点的切比雪夫距离,那么我们每次相当于要求一个瓶颈路径,直接上 $kruskal$ 重构树即可

时间复杂度 $O(n^2\log n+m\log n)$

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
#include <iostream>
#include <cmath>
#include <vector>
#include <algorithm>
#include <tuple>
#define maxm 1010
#define maxn 1000010
#define Maxn 2000010
#define ll long long
using namespace std;

int n, m, g[maxm][maxm], s[maxm][maxm], r[maxm][maxm];
char str[maxm];

inline int id(int x, int y) { return (x - 1) * n + y; }

inline bool check(int x1, int x2, int y1, int y2) {
return s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] == 0;
}

int fa[Maxn];
void init_fa(int n) { for (int i = 1; i <= n; ++i) fa[i] = i; }
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

vector<int> G[Maxn]; int w[Maxn], top, dep[Maxn], f[Maxn][21];
void dfs(int u, int fa) {
for (auto v : G[u]) {
if (v == fa) continue;
dep[v] = dep[u] + 1; f[v][0] = u; dfs(v, u);
}
}

void init_lca(int n) {
for (int j = 1; j <= 20; ++j)
for (int i = 1; i <= n; ++i) f[i][j] = f[f[i][j - 1]][j - 1];
}
int get_lca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i = 20; ~i; --i)
if (dep[f[x][i]] >= dep[y]) x = f[x][i];
if (x == y) return x;
for (int i = 20; ~i; --i)
if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}


int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> str + 1;
for (int j = 1; j <= n; ++j) g[i][j] = str[j] == '#';
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) s[i][j] += g[i][j] + s[i - 1][j];
for (int j = 1; j <= n; ++j)
for (int i = 1; i <= n; ++i) s[i][j] += s[i][j - 1];
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
if (g[i][j]) continue;
int l = 1, r = min({ i, j, n - i + 1, n - j + 1 }), mid, ans;
while (l <= r) {
mid = l + r >> 1;
if (check(i - mid + 1, i + mid - 1, j - mid + 1, j + mid - 1))
ans = mid, l = mid + 1;
else r = mid - 1;
} ::r[i][j] = ans;
}
vector<tuple<int, int, int>> vec;
int dx[] = { -1, 1, 0, 0 }, dy[] = { 0, 0, 1, -1 };
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
for (int k = 0; k < 4; ++k) {
int x = i + dx[k], y = j + dy[k];
if (x < 1 || x > n || y < 1 || y > n) continue;
if (g[i][j] || g[x][y]) continue;
if (r[i][j] > r[x][y]) continue;
vec.emplace_back(r[i][j], id(i, j), id(x, y));
}
sort(vec.begin(), vec.end()), reverse(vec.begin(), vec.end()); init_fa(2 * n * n); top = n * n;
for (auto [w, u, v] : vec) {
int fu = find(u), fv = find(v);
if (fu == fv) continue; fa[fu] = fa[fv] = ++top; ::w[top] = w;
G[top].push_back(fu), G[top].push_back(fv);
}
for (int i = 1; i <= top; ++i)
if (find(i) == i) dep[i] = 1, dfs(i, 0);
init_lca(top); cin >> m;
for (int i = 1; i <= m; ++i) {
int x1, x2, y1, y2; cin >> x1 >> y1 >> x2 >> y2;
if (g[x1][y1] || g[x2][y2]) { cout << 0 << "\n"; continue; }
int u = id(x1, y1), v = id(x2, y2);
if (find(u) != find(v)) { cout << 0 << "\n"; continue; }
int k = get_lca(u, v);
cout << 2 * w[get_lca(u, v)] - 1 << "\n";
}
return 0;
}