「BZOJ4242」水壶
传送门
Luogu团队题链接
解题思路
考虑以图中的建筑为点建一张图,那么我们就只要求出这个图的 \(\text{Kruskal}\) 重构树然后按套路搞就行了。
重点是优化连边,因为直接连边边数就会太多贼JB多。
考虑 \(\text{BFS}\),我们把所有建筑都作为源点跑 \(\text{BFS}\),求出每个点的距离他最近的建筑以及这段距离。
然后只要两个相邻点的最近建筑不同,就把这两个最近建筑连边,边数就优化到了 \(O(4\times H\times W)\) 级别(思想参照这题)
细节注意事项
- 这题我调了一上午,原因有:
太菜:并查集忘记合并。
太菜:忘记倍增。
太菜:忘记判 \(-1\) 。
参考代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
#include <queue>
#define rg register
using namespace std;
template < typename T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) s = s * 10 + (c ^ 48), c = getchar();
s = f ? -s : s;
}
typedef long long LL;
const int _ = 2002;
const int __ = 200002;
const int dx[] = { 0, 0, 1, -1 };
const int dy[] = { 1, -1, 0, 0 };
queue < pair < int, int > > Q;
int n, m, p, q, node;
int a[_][_], id[_][_]; LL dis[_][_];
int dep[__ * 2], fa[22][__ * 2]; LL val[__ * 2];
struct Edge { int x, y; LL w; }edge[_ * _ * 2]; int cnt;
inline bool cmp(const Edge& x, const Edge& y) { return x.w < y.w; }
int Fa[__ * 2];
inline int findd(int x) { return Fa[x] == x ? x : Fa[x] = findd(Fa[x]); }
inline int get(int x) { return !x || dep[x] ? dep[x] : dep[x] = get(fa[0][x]) + 1; }
inline void Kruskal() {
for (rg int i = 1; i <= p; ++i) Fa[i] = i;
sort(edge + 1, edge + cnt + 1, cmp);
node = p;
for (rg int i = 1; i <= cnt; ++i) {
int fx = findd(edge[i].x);
int fy = findd(edge[i].y);
if (fx == fy) continue;
++node;
Fa[node] = Fa[fx] = Fa[fy] = node;
fa[0][fx] = fa[0][fy] = node, val[node] = edge[i].w;
}
for (rg int i = 1; i <= node; ++i) get(i);
for (rg int i = 1; i <= 20; ++i)
for (rg int j = 1; j <= node; ++j)
fa[i][j] = fa[i - 1][fa[i - 1][j]];
}
inline LL LCA(int x, int y) {
if (findd(x) != findd(y)) return -1;
if (dep[x] < dep[y]) swap(x, y);
for (rg int i = 20; ~i; --i) if (dep[fa[i][x]] >= dep[y]) x = fa[i][x];
if (x == y) return x;
for (rg int i = 20; ~i; --i) if (fa[i][x] != fa[i][y]) x = fa[i][x], y = fa[i][y];
return val[fa[0][x]];
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
#endif
read(n), read(m), read(p), read(q);
char s[_];
for (rg int i = 1; i <= n; ++i) {
scanf("%s", s + 1);
for (rg int j = 1; j <= m; ++j) a[i][j] = s[j] == '.';
}
memset(dis, -1, sizeof dis);
for (rg int x, y, i = 1; i <= p; ++i)
read(x), read(y), Q.push(make_pair(x, y)), id[x][y] = i, dis[x][y] = 0;
while (!Q.empty()) {
pair < int, int > x = Q.front(); Q.pop();
int i = x.first, j = x.second;
for (rg int ni, nj, k = 0; k < 4; ++k) {
ni = i + dx[k], nj = j + dy[k];
if (ni < 1 || ni > n || nj < 1 || nj > m) continue;
if (!a[ni][nj]) continue;
if (id[ni][nj]) {
if (id[i][j] != id[ni][nj])
edge[++cnt] = (Edge) { id[i][j], id[ni][nj], dis[i][j] + dis[ni][nj] };
} else
dis[ni][nj] = dis[i][j] + 1, id[ni][nj] = id[i][j], Q.push(make_pair(ni, nj));
}
}
Kruskal();
for (rg int x, y; q--; ) read(x), read(y), printf("%lld\n", LCA(x, y));
return 0;
}
完结撒花 \(qwq\)