这道题要使得从一个点出发经过大小不小于 \(T\) 的连通块且要使最大的边权最小。
考虑使用 kruskal 重构树。我们建好 kruskal 重构树之后,其实就是要求包含叶子节点的数量不小于 \(T\) 且包含询问点子树的根的最小权值。
由于我们建的 kruskal 重构树是大根堆性质,所以就是求最深的那个节点。
树上倍增即可。
cpp
#include <bits/stdc++.h>
using namespace std;
const long long N = 7.2e5 + 5;
struct Edge {long long u, v, w;} e[720005];
long long h[605][605], f[N], val[N], fa[21][N], sz[N]; // sz[u] 表示 u 的字树有多少叶子
long long tot = 0, v, cnt, root;
vector<long long> g[N];
long long getf(long long x) {return x == f[x] ? x : f[x] = getf(f[x]);}
void kruskal() {
sort(e + 1, e + 1 + tot, [](Edge x, Edge y) {return x.w < y.w;});
for(long long i = 1; i <= v; i++) f[i] = i;
for(long long i = 1; i <= tot; i++) {
long long u = getf(e[i].u), v = getf(e[i].v);
if(u == v) continue;
val[++cnt] = e[i].w; // 建立新点,权值为边权
f[cnt] = f[u] = f[v] = cnt;
g[cnt].push_back(u), g[cnt].push_back(v);
fa[0][u] = fa[0][v] = cnt;
}
root = getf(1);
}
void dfs(long long u) { // 求倍增表
if(u <= v) sz[u] = 1;
for(long long i = 1; i < 21; i++) fa[i][u] = fa[i - 1][fa[i - 1][u]];
for(auto v : g[u]) dfs(v), sz[u] += sz[v];
}
int main() {
cin.tie(0)->sync_with_stdio(0);
long long n, m, t; cin >> m >> n >> t;
v = n * m; cnt = v;
for(long long i = 1, idx = 1; i <= m; i++)
for(long long j = 1; j <= n; j++, idx++) { // 网格图建边
cin >> h[i][j];
if(i > 1) e[++tot] = {idx, idx - n, abs(h[i][j] - h[i - 1][j])};
if(j > 1) e[++tot] = {idx, idx - 1, abs(h[i][j] - h[i][j - 1])};
}
kruskal(); // 求重构树
dfs(root); // 倍增
long long ans = 0;
for(long long i = 1; i <= v; i++) {
bool op; cin >> op;
if(op) {
long long u = i;
for(long long j = 20; j >= 0; j--) if(fa[j][u] && sz[fa[j][u]] < t) u = fa[j][u];
// 树上倍增
u = (fa[0][u] ? fa[0][u] : u);
ans += val[u];
}
}
cout << ans << "\n";
return 0;
}