【BZOJ 3754】Tree之最小方差树

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;
}
NOI 2017 Bless All
posted @ 2016-11-15 20:31  abclzr  阅读(171)  评论(0编辑  收藏  举报