JZOJ 1405. 电缆建设【最小生成树】


题目:

传送门


题意:

一共有n+mn+m个点,我们需要在连接长度最小的情况下使得所有点相连通


分析:

最显然的方式肯定跑最小生成树
但这样的时间复杂度肯定是承担不起的,所以我们要想点方法使得算法更加优秀
考虑nn个点中的一个点与mm个点中所有点的连线,因为题目要求的是最小长度,所以只有与这个点高度最相近的mm个点中的两个的连线才会可能是答案;对于横座标相同的点,显然只会和上下两个点相连的连线是可能是答案
按照这个规则建边就能得到4(n+m)4*(n+m)条边,如此一来时间复杂度就变得很优秀


代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#define LL long long
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}
LL Y1[600005],y2[600005];
struct node{
	int x,y;LL w;
}e[2400005];
int cnt=0;
void add(int x,int y,LL w)
{
	e[++cnt]=(node){x,y,w};
	return;
}
int f[1200005];
bool cmp(node a,node b) {return a.w<b.w;}
LL find(LL i) {return f[i]==i?i:f[i]=find(f[i]);}
int main()
{
	LL n=read(),m=read(),x1=read(),x2=read();
	for(int i=1;i<=n;i++) Y1[i]=read()+Y1[i-1];
	for(int i=1;i<=m;i++) y2[i]=read()+y2[i-1];    
	for(int i=2;i<=n;i++) add(i-1,i,(Y1[i]-Y1[i-1])*(Y1[i]-Y1[i-1])*1ll);
	for(int i=2;i<=m;i++) add(n+i-1,n+i,(y2[i]-y2[i-1])*(y2[i]-y2[i-1])*1ll);
	for(int i=1;i<=n;i++) 
	{
		LL j=lower_bound(y2+1,y2+1+m,Y1[i])-y2;
		add(i,j+n,(x2-x1)*(x2-x1)+(Y1[i]-y2[j])*(Y1[i]-y2[j]));
		if(j>1) add(i,j+n-1,(x2-x1)*(x2-x1)+(Y1[i]-y2[j-1])*(Y1[i]-y2[j-1]));
	}
	for(int i=1;i<=n+m;i++) f[i]=i;
	sort(e+1,e+1+cnt,cmp);
	double ans=0;
	for(int i=1,k=0;i<=cnt&&k<n+m-1;i++) 
	{
		LL x=e[i].x,y=e[i].y;
		if(find(x)==find(y)) continue;
		f[find(x)]=find(y);
		ans+=sqrt(e[i].w);k++;
	}
	printf("%.2lf",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章