北极通讯网络
题目
思路
一开始想的时候,先删除最大的点,然后依次枚举,找到不满足题目条件,即卫星数目不够图连通,也就是连通块的数量大于卫星数,那么这个数的上一个边权,就是我们要找的最小的\(d\), 其实这个是满足单调性的可以进行二分。
我们可以将题目抽象为:找到最小的\(d\), 满足删除所有大于\(d\)的边权后,连通块的数量不超过\(k\)。
二分的话,复杂度在\(\Theta(MlogM)\), 貌似也可以, 但是我们用\(Kruskal\)算法的时候我们发现并不需要这个,我们可以优化成\(\Theta(M)\), 用\(Kruskal\)算法来从最小边权枚举,每次并查集操作时,我们将连通块的数量\(N\)减一, 维护连通快的数量,直到满足条件。
Code
#include <bits/stdc++.h>
#define ff first
#define ss second
using i64 = long long;
const int N = 600, M = 2e5 + 10;
int n, m, k;
int p[N];
struct Edge {
int a, b;
double w;
bool operator<(const Edge &t) const {
return w < t.w;
}
}edge[M];
std::vector<std::pair<int, int>> v1;
double get_dist(int i, int j) {
int dx = v1[i].ff - v1[j].ff;
int dy = v1[i].ss - v1[j].ss;
return sqrt(std::abs(dx * dx + dy * dy));
}
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
double kruskal() {
int cnt = n;
double res = 0;
std::sort(edge + 1, edge + 1 + m);
for (int i = 1; i <= m; i ++) {
// std::cout << edge[i].w << "\n";
int a = edge[i].a, b = edge[i].b;
int fa = find(a), fb = find(b);
double w = edge[i].w;
if (fa != fb) {
if (cnt <= k) {
break;
}
p[fa] = fb;
res = w;
cnt --;
}
}
return res;
}
int main() {
std::cin >> n >> k;
v1.resize(n + 1);
for (int i = 1; i <= n; i ++) p[i] = i;
for (int i = 1; i <= n; i ++) {
std::cin >> v1[i].ff >> v1[i].ss;
}
for (int i = 1; i <= n; i ++) {
for (int j = 1; j < i; j ++) {
edge[++ m] = {i, j, get_dist(i, j)};
}
}
printf("%.2lf", kruskal());
}