bzoj3754 Tree之最小方差树 最小生成树+推性质

题目传送门

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

题解

感觉这个思路挺神仙的。

后悔没有好好观察题目的数据范围,一直把 \(n\)\(m\) 当成 1e5 来思考,\(c\) 竟然也只有 \(100\)


有了数据范围以后可以发现,边权和位于 \(nc\) 级别,大概就是 \(10000\) 左右。

所以我们可以考虑枚举边权和,从而得到边权的平均数。

然后我们给每一条边的边权赋值为 \((\)原始边权 \(-\) 平均数\()^2\)。这样求出最小生成树。

但是有一个问题就是我们求出来的最小生成树的边权和不一定就是我们枚举的边权和。

不过很容易发现如果边权和不是我们枚举的边权和,那么我们在实际的边权和的地方计算出来的结果一定比这个优。所以没有影响。

上面的结论证明的话,大概就是考虑实际边权和为 \(s\),实际平方和为 \(t\)。我们枚举的平均数为 \(v\)

\[\begin{align*} Sum &= \sum (a_i - v) ^ 2\\ &= \sum a_i^2 - 2a_iv + nv^2\\ &= t - 2sv + nv^2 \end{align*} \]

显然当 \(v = \frac sn\) 的时候最优,也就是 \(v\) 就是平均数的时候最优。


时间复杂度 \(O(ncm\log m)\)

我跑的好慢啊。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 100 + 7;
const int M = 1000 + 7;

int n, m;
int fa[N];
struct Edges { int x, y, z; double w; } e[M];
inline bool operator < (const Edges &a, const Edges &b) { return a.w < b.w; }
inline bool operator > (const Edges &a, const Edges &b) { return a.w > b.w; }

inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

inline double kruskal(bool flag = 0) {
	if (!flag) std::sort(e + 1, e + m + 1);
	else std::sort(e + 1, e + m + 1, std::greater<Edges>());
	for (int i = 1; i <= n; ++i) fa[i] = i;
	double ans = 0;
	for (int i = 1; i <= m; ++i) {
		int x = find(e[i].x), y = find(e[i].y);
		if (x == y) continue;
		fa[y] = x, ans += e[i].w;
	}
	return ans;
}

inline void work() {
	int l = kruskal(), r = kruskal(1);
	double ans = 1e10;
	for (int i = l; i <= r; ++i) {
		double v = (double)i / (n - 1);
		for (int j = 1; j <= m; ++j) e[j].w = (e[j].z - v) * (e[j].z - v);
		smin(ans, kruskal());
	}
	printf("%.4lf\n", sqrt(ans / (n - 1)));
}

inline void init() {
	read(n), read(m);
	for (int i = 1; i <= m; ++i) read(e[i].x), read(e[i].y), read(e[i].z), e[i].w = e[i].z;
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-11-01 22:25  hankeke303  阅读(213)  评论(0编辑  收藏  举报