题解 【[ARC076B] Built?】

【题目描述】

平面上有 \(N\) 个城市。第 \(i\) 个城市的坐标为 \((x_i,y_i)\) 。同一个坐标上可能有多个城市。在坐标为 \((a,b)\) 的城市和坐标为 \((c,d)\) 的城市间建造一条道路需要 \(min(|a-c|,|b-d|)\) 円。只能在城市与城市间建造道路。

要使任意两个城市之间有直接或间接道路相连,最少需要多少円?

【输入格式】

输入按以下形式:

\[N \]

\[x_1 \space y_1 \]

\[x_2 \space y_2 \]

\[: \]

\[x_N \space y_N \]

【输出格式】

请输出使任意两城市间有直接或间接道路连接所需最少钱数。

【样例输入输出】

【输入 #1】
3
1 5
3 9
7 8
【输出 #1】
3
【输入 #2】
6
8 3
4 9
12 19
18 1
13 5
7 6
【输出 #2】
8

【数据规模与约定】

  • \(2 \leq N \leq 10^5\)
  • \(0 \leq x_i,y_i \leq 10^9\)
  • 输入全为整数
【样例 \(1\) 解释】

在城市 \(1\) 与城市 \(2\) 间建造一条道路,在城市 \(2\) 与城市 \(3\) 间建造一条道路,花费 \(2+1=3\) 円。


这题是一个巧妙的图论。

先讲讲我最开始的暴力思路,对于点 \(i\) 和点 \(j\),我们在两点之间建一条长度为 \(\min(|x_i-x_j|,|y_i-y_j|)\) 的边。

一共有 \(n^2\) 条边,用 Prim 求最短路,所以空间复杂度是 \(O(n^2)\),时间复杂度是 \(O(n^2)\),对于本题 \(n \leq 10^5\) 的数据来说,卡卡常就过去了 是不可能过的。


正解思路是,对于点 \(i\) 和点 \(j\),我们构造两条边,分别是 \(|x_i-x_j|\)\(|y_i-y_j|\)

对于点 \(i\)\(j\)\(k\),如果存在 \(x_i<x_j<x_k\),因为 \((x_j-x_i)+(x_k-x_j)=x_k-x_i\),所以 \(i,k\) 之间的代价为 \(x_k-x_i\) 的边不会出现在最小生成树中。

因此,我们只要将横纵坐标排个序,然后在相邻两个数之间建边,用 Kruskal 跑最小生成树就好了。

代码如下:

#include<bits/stdc++.h>
#define rint register long long
#define int long long
using namespace std;
inline int read(){
    int s=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
    while(c>='0'&&c<='9')s=(s<<1)+(s<<3)+c-48,c=getchar();
    return f?s:-s;
}
struct hhd{
    int x,id,type;
    friend bool operator < (hhd a,hhd b){
        return a.x<b.x;
    }
}a[100010],b[100010],c[200010];
int n,m,num,ans,cnt,fa[100010];
int get_fa(int x){
    return x==fa[x]?x:fa[x]=get_fa(fa[x]);
}
void add_a(int p){
    c[++num].x=a[p].x-a[p-1].x;
    c[num].id=p-1; c[num].type=1;
}
void add_b(int p){
    c[++num].x=b[p].x-b[p-1].x;
    c[num].id=p-1; c[num].type=2;
}
void merge_a(int p){
    int id=c[p].id;
    int fa_x=get_fa(a[id].id),fa_y=get_fa(a[id+1].id);
    if(fa_x==fa_y) return;
    fa[fa_x]=fa_y; ans+=c[p].x;
    ++cnt;
}
void merge_b(int p){
    int id=c[p].id;
    int fa_x=get_fa(b[id].id),fa_y=get_fa(b[id+1].id);
    if(fa_x==fa_y) return;
    fa[fa_x]=fa_y; ans+=c[p].x;
    ++cnt;
}
signed main(){
    n=read();
    for(rint i=1;i<=n;++i){
        a[i].x=read(); b[i].x=read();
        a[i].id=b[i].id=i;
    }
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    for(rint i=2;i<=n;++i) add_a(i),add_b(i);
    sort(c+1,c+num+1);
    for(rint i=1;i<=n;++i) fa[i]=i;
    for(rint i=1;i<=num;++i){
        if(c[i].type==1) merge_a(i);
        else merge_b(i);
        if(cnt==n-1)break;
    }
    printf("%lld\n",ans);
    return 0;
}

码风很丑,见谅QVJIZ6DSFAY9Y0PP@Q_2_8R.gif

猜你喜欢

转载自www.cnblogs.com/LCGUO/p/12661386.html