题目传送门

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;
}

bzoj3754 Tree之最小方差树 最小生成树+推性质的更多相关文章

  1. [BZOJ3080]Minimum Variance Spanning Tree/[BZOJ3754]Tree之最小方差树

    [BZOJ3080]Minimum Variance Spanning Tree/[BZOJ3754]Tree之最小方差树 题目大意: 给定一个\(n(n\le50)\)个点,\(m(m\le1000 ...

  2. [BZOJ3754]Tree之最小方差树

    3754: Tree之最小方差树 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 402  Solved: 152[Submit][Status][Di ...

  3. 【bzoj3754】Tree之最小方差树 最小生成树

    题目描述 给出一张无向图,求它的一棵生成树,使得选出的所有边的方差最小.输出这个最小方差. 输入 第一行两个正整数N,M 接下来M行,每行三个正整数Ui,Vi,Ci N<=100,M<=2 ...

  4. 【枚举】【最小生成树】【kruscal】bzoj3754 Tree之最小方差树

    发现,若使方差最小,则使Σ(wi-平均数)2最小即可. 因为权值的范围很小,所以我们可以枚举这个平均数,每次把边权赋成(wi-平均数)2,做kruscal. 但是,我们怎么知道枚举出来的平均数是不是恰 ...

  5. bzoj 3754: Tree之最小方差树 模拟退火+随机三分

    题目大意: 求最小方差生成树.N<=100,M<=2000,Ci<=100 题解: 首先我们知道这么一个东西: 一些数和另一个数的差的平方之和的最小值在这个数是这些数的平均值时取得 ...

  6. 【BZOJ 3754】: Tree之最小方差树

    题目链接: TP 题解: 都是骗子233,我还以为是什么神奇的算法. 由于边权的范围很小,最小生成树和最大生成树之间的总和差不会太大,所以可以枚举边权和,再直接根据方差建最小生成树,每次更新答案即可. ...

  7. BZOJ 3754 Tree之最小方差树 MST

    Description Wayne 在玩儿一个很有趣的游戏.在游戏中,Wayne 建造了N 个城市,现在他想在这些城市间修一些公路,当然并不是任意两个城市间都能修,为了道路系统的美观,一共只有M 对城 ...

  8. 【BZOJ 3754】Tree之最小方差树

    http://www.lydsy.com/JudgeOnline/problem.php?id=3754 核心思想:暴力枚举所有可能的平均数,对每个平均数排序后Kruskal. 正确的答案一定是最小的 ...

  9. BZOJ 3754 Tree之最小方差树

    枚举平均数. mdzz编译器. #include<iostream> #include<cstdio> #include<cstring> #include< ...

随机推荐

  1. reduce、map、zip、filter使用记录

    注意:结果取完一次就没了: # -*- coding:utf-8 -*- ### functools.reduce from functools import reduce r1 = reduce(l ...

  2. Mysql-5.7 x64安装

    首先在官网下载Mysql:https://dev.mysql.com/downloads/mysql/ 选择ZIP Archive下载. 下载安装之后配置环境变量: 编辑现有环境变量Path: PS: ...

  3. C# 实现IDisposable

    #region 实现IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this);//防止Finalize调 ...

  4. yum 下载rpm包 安装rpm包依赖关系

    方法一:yumdownloader 工具 1.安装工具包 yum install yum-utils -y 2.下载一个RPM包 yumdownloader <package-name> ...

  5. python 使用 with open() as 读写文件

    读文件: 要以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符: >>> f = open('E:\python\python\test.tx ...

  6. PHPFPM模式三种运行模式

    1.static模式 static模式始终会保持一个固定数量的子进程,这个数量由pm.max_children定义.   2.dynamic模式 子进程的数量是动态变化的,启动时,会生成固定数量的子进 ...

  7. xmake v2.2.9 发布, 新增c++20 modules的实验性支持

    这个版本没啥太大新特性,主要对c++20 modules进行了实验性支持,目前支持clang/msvc编译器,除此之外改进了不少使用体验,并且提高了一些稳定性. 另外,这个版本新增了socket.io ...

  8. argparse命令行传参

    import argparse parser = argparse.ArgumentParser(description='manual to this script') # 创建解析器,及其描述 p ...

  9. 【Linux 环境搭建】Ubuntu下安装tftp

    1.安装软件    sudo apt-get install tftp-hpa tftpd-hap xinetd 2.修改配置文件    sudo vim /etc/default/tftpd-hpa ...

  10. linux服务器上安装mysql

    mysql版本:mysql-5.6.44-linux-glibc2.12-x86_64.tar linux操作系统和版本信息: 1.检查linux服务器上是否已安全mysql [root@localh ...