zoukankan      html  css  js  c++  java
  • [DarkBZOJ3754] Tree之最小方差树

    前言

    感觉如果不讲做法应该要想上好一会,甚至可能做不出来。

    题目

    DarkBZOJ

    讲解

    拿到这道题很有可能没什么思路,我们就从暴力入手。

    先把标准差的公式写出来:

    [sqrt{frac{sum_{0le i<n}(a_i-overline{a})^2}{n}} ]

    对于这道题,显然我们知道生成树是 (n-1) 条边,而且外面的根号其实不影响相对大小,所以我们去掉这些东西( (a_i) 为边权):

    [sum (a_i-overline{a})^2 ]

    显然我们如果知道了 (overline{a}) 的大小,就可以通过最小生成树算法求出上面这个东西的最小值,可惜我们不知道。

    但我们真的不能知道吗?

    写出它的表达式:(overline{a}=frac{sum a_i}{n-1}) ,显然 (overline{a})(sum a_i) 决定,我们发现 (sum a_i) 最多不过 (10^4),直接枚举即可。

    但是这还不够,如果你枚举一次做一次 ( t Kruskal),发现复杂度达到了 (O(cnmlog_2m)),是有问题的。

    其实我们只需要一次排序,枚举一次的时候用双指针维护一下当前情况下权值 ((a_i-overline{a})) 最小,且原始权值 (a_i) 小于和大于平均值的边,并不难实现。

    于是复杂度就是 (O(mlog_2m+cnm))

    我真不想说脏话。(摊手

    代码

    不懂戳我
    //12252024832524
    #include <cmath>
    #include <cstdio>
    #include <cstring> 
    #include <algorithm>
    #define TT template<typename T>
    using namespace std;
    
    typedef long long LL;
    const int MAXN = 105;
    const int MAXM = 2005;
    const int INF = 0x3f3f3f3f;
    int n,m;
    double ans = INF;
    
    LL Read()
    {
    	LL x = 0,f = 1; char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1) 
    {
    	if(x < 0) x = -x,putchar('-');
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    struct edge
    {
    	int u,v,w;
    	bool operator < (const edge &px)const{
    		return w < px.w;
    	}
    }e[MAXM];
    
    int f[MAXN];
    int findSet(int x){if(x^f[x])f[x]=findSet(f[x]);return f[x];}
    void unionSet(int u,int v){f[findSet(u)] = findSet(v);}
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read(); m = Read();
    	for(int i = 1;i <= m;++ i) e[i].u = Read(),e[i].v = Read(),e[i].w = Read();
    	sort(e+1,e+m+1);
    	e[0].w = -INF; e[m+1].w = INF;
    	for(int C = 1;C <= 100*(n-1);++ C)
    	{
    		for(int i = 1;i <= n;++ i) f[i] = i;
    		int cnt = n-1,s = 0; double av = C / (n-1.0),cur = 0;
    		int r = upper_bound(e+1,e+m+2,edge{0,0,(int)ceil(av)}) - e;
    		int l = r-1;
    		while(cnt)
    		{
    			while(l > 0 && findSet(e[l].u) == findSet(e[l].v)) --l;
    			while(r <= m && findSet(e[r].u) == findSet(e[r].v)) ++r;
    			double L = av-e[l].w,R = e[r].w-av;
    			if(L < R) cur += L*L,s += e[l].w,unionSet(e[l].u,e[l].v),--l;
    			else cur += R*R,s += e[r].w,unionSet(e[r].u,e[r].v),++r;
    			--cnt;
    		}
    		if(s == C) ans = Min(ans,sqrt(cur/(n-1)));
    	}
    	printf("%.4f",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java实现 洛谷 P1060 开心的金明
    (Java实现) 洛谷 P1605 迷宫
    (Java实现) 洛谷 P1605 迷宫
    (Java实现)洛谷 P1093 奖学金
    (Java实现)洛谷 P1093 奖学金
    Java实现 洛谷 P1064 金明的预算方案
    Java实现 洛谷 P1064 金明的预算方案
    (Java实现) 洛谷 P1031 均分纸牌
    QT树莓派交叉编译环开发环境搭建(附多个exe工具下载链接)
    武则天红人对唐睿宗的桃色报复(如此缺少城府,注定了要在宫廷中过早地出局)
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15362475.html
Copyright © 2011-2022 走看看