NOIP模拟 飞越行星带(并查集)
内网传送门
【题目分析】
考虑一个非法直径,如果将所有距离小于该直径的点连边(上下界缩为一个点),那么一定存在一条路径联通上下界。
所以用类似kruskal的做法,先将这些边排序,然后从小到大加边,如果加到一条边使得上下界联通,那么边权即为答案。
【代码~】
#include<bits/stdc++.h>
using namespace std;
const int MAXN=510;
const int MAXM=1e5+10;
int n,cnt;
double l;
double x[MAXN],y[MAXN];
int fa[MAXN];
struct Edge{
int from,to;
double w;
friend inline bool operator<(const Edge &a,const Edge &b){
return a.w<b.w;
}
}edge[MAXM];
int Read(){
int i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
void add(int x,int y,double z){
edge[cnt].from=x,edge[cnt].to=y,edge[cnt].w=z,cnt++;
}
int find(int x){
if(x==fa[x])
return x;
return fa[x]=find(fa[x]);
}
double dis(int a,int b,int c,int d){
return sqrt((a-c)*(a-c)+(b-d)*(b-d));
}
int main(){
n=Read();
cin>>l;
for(int i=1;i<=n;++i){
scanf("%lf%lf",&x[i],&y[i]);
for(int j=1;j<i;++j)
add(i,j,dis(x[i],y[i],x[j],y[j]));
add(i,0,y[i]);
add(i,n+1,l-y[i]);
}
for(int i=0;i<=n+1;++i)
fa[i]=i;
sort(edge,edge+cnt+1);
for(int i=1;i<=cnt;++i){
int u=edge[i].from,v=edge[i].to;
u=find(u),v=find(v);
if(u==v)
continue;
fa[v]=u;
if(find(0)==find(n+1)){
printf("%.3lf",edge[i].w);
return 0;
}
}
return 0;
}