BZOJ 4242: 水壶(Kruskal重构树 + Bfs)

题意

一块 hw 的区域,存在障碍、空地、n 个建筑,从一个建筑到另一个建筑的花费为:路径上最长的连续空地的长度。

q 次询问:从建筑 siti 的最小花费。

h,w2×103,n,q2×105

题解

对于任意两个建筑把它们之间的只走空地的最短路长度作为权值,然后做最小生成树。

如果搞出了最小生成树,那么就只需在 kruskal 重构树上求 LCA 就行了,因为 LCA 的权值是路上所有边的最大权值。

如果不会可以参考 「NOI2018」归程(Dijkstra + Kruskal重构树 + 倍增) 这题。

然而边数达到 O(n2) ,暴力求边需要 O(nhw)

把所有建筑一起作为源点,跑 bfs ,可以得到离每个位置最近的建筑及距离。

然后,如果两个相邻位置的最近建筑不同,那么就将这对建筑连边,边数就降成 O(hw) 的。

对于一个点如果存在多个最近的点,我们其实只需要把所有点连向第一个 bfs 到这个的点就行了,可以证明这是对的。(能自己画图理解)

所以最后复杂度就是 O(hwlog(hw)+qlogn)

总结

这种路径上 最大 / 最小 作为权值的题,常常可以考虑 kruskal 重构树来做。

平面上连边常常可以找特殊点来减少边数。

代码

/************************************************************** Problem: 4242 User: DOFY Language: C++ Result: Accepted Time:32156 ms Memory:271236 kb ****************************************************************/ #include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) #define fir first #define sec second #define mp make_pair using namespace std; typedef pair<int, int> PII; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;} template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("4242.in", "r", stdin); freopen ("4242.out", "w", stdout); #endif } const int Maxn = 2010, N = 4e5 + 1e3; bool G[Maxn][Maxn]; char str[Maxn]; queue<PII> Q; int id[Maxn][Maxn], dis[Maxn][Maxn]; struct Edge { int u, v, w; } lt[Maxn * Maxn * 4]; struct Cmp { inline bool operator () (const Edge &lhs, const Edge &rhs) const { return lhs.w < rhs.w; } }; int fa[N]; int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } int h, w, p, q; const int dir[4][2] = { {0, -1}, {0, 1}, {1, 0}, {-1, 0} }; int dep[N], val[N], anc[N][21], Log[N], Size; int Get_Dep(int u) { if (!u || dep[u]) return dep[u]; return dep[u] = Get_Dep(anc[u][0]) + 1; } int tot = 0; void Build_Kruskal() { For (i, 1, Size = p) fa[i] = i; sort(lt + 1, lt + tot + 1, Cmp()); For (i, 1, tot) { int u = find(lt[i].u), v = find(lt[i].v), w = lt[i].w; if (u == v) continue ; val[++ Size] = w; fa[Size] = anc[u][0] = fa[u] = anc[v][0] = fa[v] = Size; } For (i, 1, Size) { if (i > 1) Log[i] = Log[i >> 1] + 1; dep[i] = Get_Dep(i); } For (j, 1, Log[Size]) For (i, 1, Size) anc[i][j] = anc[anc[i][j - 1]][j - 1]; } inline int Calc(int u, int v) { if (find(u) != find(v)) return -1; if (dep[u] < dep[v]) swap(u, v); int gap = dep[u] - dep[v]; For (i, 0, Log[gap]) if (gap >> i & 1) u = anc[u][i]; if (u == v) return val[u]; Fordown (i, Log[dep[u]], 0) if (anc[u][i] != anc[v][i]) u = anc[u][i], v = anc[v][i]; return val[anc[u][0]]; } int main () { File(); h = read(), w = read(), p = read(), q = read(); For (i, 1, h) { scanf ("%s", str + 1); For (j, 1, w) G[i][j] = str[j] == '.'; } Set(dis, -1); For (i, 1, p) { int x = read(), y = read(); Q.push(mp(x, y)); id[x][y] = i; dis[x][y] = 0; } while (!Q.empty()) { PII u = Q.front(); Q.pop(); For (i, 0, 3) { register int x = u.fir + dir[i][0], y = u.sec + dir[i][1]; if (!G[x][y]) continue ; if (id[x][y]) { if (id[x][y] != id[u.fir][u.sec]) lt[++ tot] = (Edge){id[x][y], id[u.fir][u.sec], dis[x][y] + dis[u.fir][u.sec]}; } else { dis[x][y] = dis[u.fir][u.sec] + 1; id[x][y] = id[u.fir][u.sec]; Q.push(mp(x, y)); } } } Build_Kruskal(); For (i, 1, q) printf ("%d\n", Calc(read(), read())); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9762966.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(280)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示