Built? (最小生成树) (2023-02-09)

题目链接: https://vjudge.net/contest/538552#problem/F

题意

给出 n 个城镇的坐标 (xi, yi),在两个城镇 (i, j) 之间修路的代价为 min(|xi-xj|, |yi-yj|)

求将所有城镇连通的最小代价。

2≤N≤1e5, 0≤xi​,yi​≤1e9

思路

所有城镇之间的最短路要么是在横坐标上两两相邻,要么在纵坐标上两两相邻。将这些边加入候选,求一次最小生成树即可。

复杂度 O(n·logn)

代码

#include <vector>
#include <iostream>
#include <algorithm>

#define orep(i,l,r) for(auto i = (l); i < (r); ++i)
#define NL          std::cout << '\n';

const int maxN = 2e5+5;
int par[maxN];
int find(const int x) { return x==par[x] ? x : (par[x]=find(par[x])); }
void merge(const int x, const int y) { const int u = find(x), v = find(y); if (u!=v) { par[u] = v; } }

struct pt { int x,y,id; };
bool cmpx(const pt a, const pt b) { return a.x < b.x; }
bool cmpy(const pt a, const pt b) { return a.y < b.y; }
struct edge { int from, to, w; } edges[maxN];
bool operator<(const edge a, const edge b) { return a.w < b.w; }
int cnt;

int main() {
  std::ios::sync_with_stdio(0); std::cin.tie(0);
  int n; std::cin >> n;
  std::vector<pt> pts;
  orep(i, 0, n) {
    int x, y; std::cin >> x >> y;
    pts.push_back({x,y,i});
    par[i] = i;
  }
  std::sort(pts.begin(), pts.end(), cmpx);
  for(int pos = 1; pos != n; ++pos) {
    edges[cnt].from = pts[pos-1].id;
    edges[cnt].to = pts[pos].id;
    edges[cnt].w = pts[pos].x-pts[pos-1].x;
    ++cnt;
  }
  std::sort(pts.begin(), pts.end(), cmpy);
  for(int pos = 1; pos != n; ++pos) {
    edges[cnt].from = pts[pos-1].id;
    edges[cnt].to = pts[pos].id;
    edges[cnt].w = pts[pos].y-pts[pos-1].y;
    ++cnt;
  }
  std::sort(edges, edges+cnt);
  long long rst = 0;
  orep(i, 0, cnt) {
    if (find(edges[i].from) == find(edges[i].to)) { continue; }
    merge(edges[i].from, edges[i].to);
    rst += edges[i].w;
  }
  std::cout << rst;
  
  return 0;
}