简要题意

\(n\) 个点,每个点有两个属性 \((x,y)\)。连无向边 \((A,B)\) 的代价是 \(\min(|A_x-B_x|,|A_y,B_y|)\)

你需要连接这 \(n\) 个点,使得它们任意两点之间连通,求最小代价。

\(2 \leq n \leq 10^5\)

思路

代价带 \(\min\) 不好处理,我们考虑将代价的 \(\min\) 拆开,改成连两条边,一条边代价是 \(|A_x-B_x|\),一条边代价是 \(|A_y-B_y|\)。然后答案是两个方案的并的最小生成树。

然后我们就可以考虑如何求出只连 \(|A_x-B_x|\) 的方案?我们可以按 \(x\) 排序,然后依次连出一条链。这样最忧。

对于 \(y\),我们也可以按 \(y\) 排序,然后依次连出一条链。

最后把这两条连组合起来,形成一个无向图,求出最小边权和即可。

时间复杂度 \(O(n\log n)\)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

struct edge{
    int u,v,w,id;
} g[1000005],a[1000005];
int n,tot;

int fa[1000005];
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].u>>a[i].v;
        a[i].id=i;
    }
    sort(a+1,a+n+1,[](edge x,edge y){
        return x.u<y.u;
    });
    for(int i=1;i<n;i++){
        g[++tot]={a[i].id,a[i+1].id,(a[i+1].u-a[i].u)};
    }
    sort(a+1,a+n+1,[](edge x,edge y){
        return x.v<y.v;
    });
    for(int i=1;i<n;i++){
        g[++tot]={a[i].id,a[i+1].id,(a[i+1].v-a[i].v)};
    }
    sort(g+1,g+tot+1,[](edge x,edge y){
        return x.w<y.w;
    });
    int ans=0,cnt=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=tot;i++){
        int u=g[i].u,v=g[i].v,w=g[i].w;
        assert(g[i].id==0);
        if(find(u)==find(v)){
            continue;
        }
        ans+=w;
        fa[find(u)]=find(v);
        cnt++;
        if(cnt>=(n-1)) break;
    }
    cout<<ans;
    return 0;
}