传送门
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\)