题目:
题意:
一共有个点,我们需要在连接长度最小的情况下使得所有点相连通
分析:
最显然的方式肯定跑最小生成树
但这样的时间复杂度肯定是承担不起的,所以我们要想点方法使得算法更加优秀
考虑个点中的一个点与个点中所有点的连线,因为题目要求的是最小长度,所以只有与这个点高度最相近的个点中的两个的连线才会可能是答案;对于横座标相同的点,显然只会和上下两个点相连的连线是可能是答案
按照这个规则建边就能得到条边,如此一来时间复杂度就变得很优秀
代码:
#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;
}