原题链接
NKOJ-P5348
题目分析
这道题我考场上一看,不就是个最小生成树模板题,两个点之间建一条边,写了个库鲁斯卡尔(蒟蒻写不出英文)交上去,就……TLE&MLE 了;
为什么过不了?看一下数据范围: 0 < n < 1 0 5 0 < n < 10^5 0<n<105, n 2 n^2 n2暴力 肯定超时;
那我们再来看一下建桥的代价: m i n ( ∣ x i − x j ∣ , ∣ y i − y j ∣ ) min(|x_i-x_j|,|y_i-y_j|) min(∣xi−xj∣,∣yi−yj∣),不难发现,在同一水平线or竖直线上的小岛建桥代价为0,那我们就可以把这些点合并成一个点看待(即点之间不连边)
这样题目思路就非常清晰了:
先把每个点的X,Y坐标记录下来,然后用X坐标来排序,排完序后连续的两点之间才连边,代价为其X坐标之差,接着用Y坐标来排序,排完序后连续的两点之间连边,代价为其Y坐标之差,最后跑最小生成树即可;
Ac Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,ans,father[114514];
struct lyt{
int x,y,tip;
}disx[114514],disy[191981];
struct lxy{
int to;int be;int data;
}dis[1919810];
bool cmp(lxy a,lxy b){return a.data<b.data;}
bool cmpx(lyt a,lyt b){return a.x<b.x;}
bool cmpy(lyt a,lyt b){return a.y<b.y;}
int getfather(int x){
if(father[x]==x){return x;}
return father[x]=getfather(father[x]);
}
bool onion(int a,int b){
int fa=getfather(a);
int fb=getfather(b);
if(fa==fb){return false;}
father[fb]=fa;
return true;
}
void prim(){
sort(dis+1,dis+m+1,cmp);
for(int i=1;i<=m;i++){
if(onion(dis[i].to,dis[i].be)){ans+=dis[i].data;}
}
return;
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&disx[i].x,&disx[i].y);father[i]=i;
disx[i].tip=disy[i].tip=i;disy[i].x=disx[i].x;disy[i].y=disx[i].y;
}
sort(disx+1,disx+n+1,cmpx);sort(disy+1,disy+n+1,cmpy);
for(int i=2;i<=n;i++){
dis[++m].to=disx[i-1].tip;
dis[m].be=disx[i].tip;
dis[m].data=disx[i].x-disx[i-1].x;
}
for(int i=2;i<=n;i++){
dis[++m].to=disy[i-1].tip;
dis[m].be=disy[i].tip;
dis[m].data=disy[i].y-disy[i-1].y;
}
prim();
printf("%lld\n",ans);
return 0;
}