题目大意:
教主上电视了,但是蔚蓝城郊区沿河的村庄却因电缆线路老化而在直播的时候停电,这让市长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);
}