题意
平面上有
N
N
N 个城市。第
i
i
i 个城市的坐标为
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi) 。同一个坐标上可能有多个城市。在坐标为
(
a
,
b
)
(a,b)
(a,b) 的城市和坐标为
(
c
,
d
)
(c,d)
(c,d) 的城市间建造一条道路需要
m
i
n
(
∣
a
−
c
∣
,
∣
b
−
d
∣
)
min(|a-c|,|b-d|)
min(∣a−c∣,∣b−d∣) 円。只能在城市与城市间建造道路。
要使任意两个城市之间有直接或间接道路相连,最少需要多少円?
数据范围
•
2
≤
N
≤
1
0
5
2 \leq N \leq 10^5
2≤N≤105
•
0
≤
x
i
,
y
i
≤
1
0
9
0 \leq x_i,y_i \leq 10^9
0≤xi,yi≤109
• 输入全为整数
思路
很容易就能想到这题就是在问最小生成树的长度(其中两个点所连的边的边权为 m i n ( ∣ 横坐标之差 ∣ , ∣ 纵坐标之差 ∣ ) min(|横坐标之差|,|纵坐标之差|) min(∣横坐标之差∣,∣纵坐标之差∣)),但是数据范围又提醒我们如果所有边都去遍历一遍势必会超时,所以我们只能选择其中的一些边。
如果把横纵坐标拆开来看呢?考虑平面内横坐标互不相同的三点A、B、C(横坐标依次递增),若只考虑横坐标则连接AB和BC的两条边的边权势必会小于等于AC的边权——此时可以说在横坐标上AC这条边被淘汰了;也就是说,在横坐标方面我们只需要考虑相邻两个点所连的一条边即可(纵坐标也同理),这样一个点最多会连4条边,最多 4 × 1 0 5 4×10^5 4×105条边,是可以接受的。
最后用Kruskal算法来得到最小生成树,问题解决。
代码
#include <iostream>
#include <algorithm>
#define MAXN 100005
using namespace std;
int n;
long long ans;
struct P {
int x, y;
int num; //由于排序会把下标打乱(这样边的两端就乱了),因此我们需要一个不变编号
void read() {
cin >> x >> y;
}
} p[MAXN];
struct Edge {
int u, v;
long long w;
} eg[MAXN << 2]; //一个点最多和四个点相连
int U; //每个点的第一条边
int f[MAXN]; //父节点
void init() {
for (int i = 1; i <= n; ++i) {
f[i] = i;
}
}
int find_f(int x) {
return f[x] == x ? x : f[x] = find_f(f[x]);
}
bool xcmp(P p1, P p2) {
return p1.x < p2.x;
}
bool ycmp(P p1, P p2) {
return p1.y < p2.y;
}
bool wcmp(Edge e1, Edge e2) {
return e1.w < e2.w;
}
void link(P p1, P p2) { //增加一条无向边
U ++;
eg[U].u = p1.num;
eg[U].v = p2.num;
eg[U].w = min(abs(p2.x - p1.x), abs(p2.y - p1.y));
}
int main() {
cin >> n;
init();
for (int i = 1; i <= n; ++i) {
p[i].read();
p[i].num = i;
}
sort(p + 1, p + 1 + n, xcmp);
for (int i = 1; i < n; ++i) {
link(p[i], p[i + 1]);
}
sort(p + 1, p + 1 + n, ycmp);
for (int i = 1; i < n; ++i) {
link(p[i], p[i + 1]);
}
sort(eg + 1, eg + 1 + U, wcmp);
for (int i = 1; i <= U; ++i) {
int u = eg[i].u;
int v = eg[i].v;
int uu = find_f(u);
int vv = find_f(v);
if (uu == vv)
continue; //说明它们已经可以连接了,无需再去连
ans += eg[i].w;
f[uu] = vv;
}
cout << ans;
return 0;
}