【GMOJ 1405】【Vijos 1466】 电缆建设

题目大意:

教主上电视了,但是蔚蓝城郊区沿河的村庄却因电缆线路老化而在直播的时候停电,这让市长SP先生相当的愤怒,他决定重修所有电缆,并改日播放录像,杜绝此类情况再次发生。
  河流两旁各有n,m个村庄,每个村庄可以用二维坐标表示,其中河流一旁的村庄横坐标均为x1,河流另一旁的村庄横坐标均为x2。由于地势十分开阔,任意两个村庄可以沿坐标系直线修建一条电缆连接,长度即为两村庄的距离。要修建若干条电缆,使得任意两个村庄都可以通过若干个有电缆连接的村庄相连。
  因为修建的经费与长度成正比,SP市长当然希望所花的钱越少越好,所以他希望你来帮助他设计一套方案,使得电缆总长度最小,并告诉所需要的电缆总长度。

解题思路:

Kruskal
显然暴力建边会T, 所以重点是建边
对于每个在河流左边的村庄,我们将它与离它最近的一个点相连,然后再与离它最近的两个点的上下两个点相连
显然每一个村庄都要连它上下两个村庄除了最上面的一个和最下面的一个
然后跑一边Kruskal就好了

A c c e p t e d   c o d e : Accepted\ code: Accepted code:

#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 600005;

struct Line {
	int from, to;
	double w;
}e[N*10];

int n, m, x1, x2, cnt;
double ans;
int a[N], b[N], fa[N<<1];

inline int read() {
	int f = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) f = f * 10 + c - 48, c = getchar();
	return f;
}

int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
inline void add(int x, int y, double w) { e[++cnt] = (Line){x, y, w}; }
inline bool cmp(Line x, Line y) { return x.w < y.w; }

double dis(int x, int y) {
	return sqrt((double)(x1-x2) * (x1-x2) + (double)(a[x]-b[y]) * (a[x]-b[y]));
}


void kruskal() {
	int sum = n + m - 1;
	for (int i = 1; i <= cnt && sum; ++i) {
		int x = find(e[i].from);
		int y = find(e[i].to);
		if (x != y)
			fa[x] = fa[y], --sum, ans += e[i].w;
	}
}

int main() {
	n = read(), m = read(), x1 = read(), x2 = read();
	for (int i = 1; i <= n; ++i)
		a[i] = read() + a[i-1], fa[i] = i;
	for (int i = 1; i <= m; ++i)
		b[i] = read() + b[i-1], fa[i+n] = i + n;
	for (int i = 1; i < m; ++i)
		add(i + n, i + n + 1, b[i+1] - b[i]);
	for (int i = 1, j = 1; i <= n; ++i) {
		while (j < m && dis(i, j) > dis(i, j + 1)) ++j;
		add(i, j + n, dis(i, j));
		if (i < n) add(i, i + 1, a[i+1] - a[i]);
		if (j > 1) add(i, j + n - 1, dis(i, j - 1));
		if (j < m) add(i, j + n + 1, dis(i, j + 1));
	}
	sort(e + 1, e + cnt + 1, cmp);
	kruskal();
	printf("%.2lf", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值