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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章