题解 AT2643 【[ARC076B] Built?】
思路
暴力的话就\(n^2\)建边,然后跑一遍最小生成树
思考怎么优化,有一个很显然的性质,答案一定是在横坐标相邻的点之间或者纵坐标相邻的点之间连边,没有比这种方法更优的连边方法,也就是如果\(x_a\) \(<\) \(x_b\) \(<\) \(x_c\),那么对于横坐标而言这个b点不可能作出贡献,纵坐标同理,所以我们将点分别按横纵坐标排序,预处理出来每条边,然后跑一次kruskal最小生成树就好了
代码实现
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn = 1e5 + 50;
inline int read () {
int x = 0, f = 1; char ch = getchar();
for (;!isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
return x * f;
}
int n, fa[maxn], tot;
struct Node { int id, x, y; } node[maxn];
inline bool cmp1 (Node A, Node B) { return A.x < B.x; }
inline bool cmp2 (Node A, Node B) { return A.y < B.y; }
inline int find_root (int a) { return fa[a] == a ? a : fa[a] = find_root(fa[a]); }
inline void merge (int a, int b) { fa[find_root(a)] = find_root(b); }
struct Edge { int from, to, val; } edge[maxn << 1];
inline bool cmp3 (Edge A, Edge B) { return A.val < B.val; }
int main () {
n = read();
for (register int i = 1; i <= n; i++) fa[i] = i;
for (register int i = 1; i <= n; i++) node[i].id = i, node[i].x = read(), node[i].y = read();
sort (node + 1, node + 1 + n, cmp1);
for (register int i = 1; i < n; i++) {
edge[++tot].from = node[i].id, edge[tot].to = node[i + 1].id;
edge[tot].val = min (abs(node[i].x - node[i + 1].x), abs (node[i].y - node[i + 1].y));
}
sort (node + 1, node + 1 + n, cmp2);
for (register int i = 1; i < n; i++) {
edge[++tot].from = node[i].id, edge[tot].to = node[i + 1].id;
edge[tot].val = min (abs(node[i].x - node[i + 1].x), abs (node[i].y - node[i + 1].y));
}
sort (edge + 1, edge + 1 + tot, cmp3);
int mtot = 0;
long long ans = 0;
for (register int i = 1; i <= tot; i++) {
if (mtot == n - 1) break;
int x = edge[i].from, y = edge[i].to;
if (find_root(x) == find_root(y)) continue;
mtot++;
merge (x, y);
ans += edge[i].val;
}
cout<<ans<<endl;
return 0;
}