題目:
題意:
一共有個點,我們需要在連接長度最小的情況下使得所有點相連通
分析:
最顯然的方式肯定跑最小生成樹
但這樣的時間複雜度肯定是承擔不起的,所以我們要想點方法使得算法更加優秀
考慮個點中的一個點與個點中所有點的連線,因爲題目要求的是最小長度,所以只有與這個點高度最相近的個點中的兩個的連線纔會可能是答案;對於橫座標相同的點,顯然只會和上下兩個點相連的連線是可能是答案
按照這個規則建邊就能得到條邊,如此一來時間複雜度就變得很優秀
代碼:
#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;
}