題目大意:
教主上電視了,但是蔚藍城郊區沿河的村莊卻因電纜線路老化而在直播的時候停電,這讓市長SP先生相當的憤怒,他決定重修所有電纜,並改日播放錄像,杜絕此類情況再次發生。
河流兩旁各有n,m個村莊,每個村莊可以用二維座標表示,其中河流一旁的村莊橫座標均爲x1,河流另一旁的村莊橫座標均爲x2。由於地勢十分開闊,任意兩個村莊可以沿座標系直線修建一條電纜連接,長度即爲兩村莊的距離。要修建若干條電纜,使得任意兩個村莊都可以通過若干個有電纜連接的村莊相連。
因爲修建的經費與長度成正比,SP市長當然希望所花的錢越少越好,所以他希望你來幫助他設計一套方案,使得電纜總長度最小,並告訴所需要的電纜總長度。
解題思路:
Kruskal
顯然暴力建邊會T, 所以重點是建邊
對於每個在河流左邊的村莊,我們將它與離它最近的一個點相連,然後再與離它最近的兩個點的上下兩個點相連
顯然每一個村莊都要連它上下兩個村莊除了最上面的一個和最下面的一個
然後跑一邊Kruskal就好了
#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);
}