「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\)

posted @ 2019-10-30 11:57  Sangber  阅读(186)  评论(1编辑  收藏  举报