【BZOJ 3754】Tree之最小方差树

时间:2024-04-14 14:24:25

http://www.lydsy.com/JudgeOnline/problem.php?id=3754

核心思想:暴力枚举所有可能的平均数,对每个平均数排序后Kruskal。

正确的答案一定是最小的,枚举到正确的平均数后一定会算出正确答案。

枚举的平均数太多了,险些TLE。每两个相邻的整数\(a\),\(b\)\((a<b)\)之间枚举\(a+\frac13\),\(a+\frac23\)两个值作为平均数就可以了(虽然不是正确的答案的平均数,但和正确的答案的平均数排序之后的序列是相同的)。我并没有这么做QAQ因为我懒得再改了_(:з」∠)_

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 103;
const int M = 2003; int now, mn, ma, n, m, tot = 0, cnt, fa[N], a[N], suma;
double ab, S, ans = -1;
struct Edge {
int u, v, e;
double w;
Edge(int _u = 0, int _v = 0, int _e = 0, double _w = 0)
: u(_u), v(_v), e(_e), w(_w) {}
bool operator < (const Edge &A) const {
return w < A.w;
}
} G[M]; int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);} double sqr(double x) {return x * x;} int u, v, e; double Kru() {
for (int i = 1; i <= n; ++i) fa[i] = i;
cnt = 0; S = 0;
for (int i = 1; i <= tot; ++i) {
u = find(G[i].u); v = find(G[i].v);
if (u != v) {
++cnt;
fa[u] = v;
S += G[i].w;
if (cnt == n - 1)
break;
}
}
return S;
} int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &u, &v, &e);
G[++tot] = Edge(u, v, e, (double) e);
} stable_sort(G + 1, G + tot + 1);
mn = (int) Kru();
reverse(G + 1, G + tot + 1);
ma = (int) Kru(); for (now = mn; now <= ma; ++now) {
ab = (double) now / (n - 1);
for (int i = 1; i <= tot; ++i)
G[i].w = sqr(ab - G[i].e);
stable_sort(G + 1, G + tot + 1);
S = Kru();
ans = ans == -1 ? S : min(ans, S);
} printf("%.4lf\n", sqrt(ans / (n - 1)));
return 0;
}