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;
}

 

posted @ 2018-11-02 16:11  Ishtar~  阅读(162)  评论(0编辑  收藏  举报