CF437D The Child and Zoo

题意:

题面

分析:

一句话题意:求任意两点之间各条简单路径上最小值的最大值的平均数(我好像也没说人话

这题的 (n) 极大,所以我们没有办法优化正常的暴力,所以我们考虑将式子拆开来计算,我们考虑每一个点在什么情况下会被选,那肯定在当它是一对点各条简单路径上最小值的最大值的时候,我们发现各条简单路径的最小值这个条件过于恶心,那么思考怎么消掉这个条件

我们只需要将任意两点通过它们路径上点权的最小值连接起来就可以了,由于点权无法连接两个点,那么我们将点权压到边权上去,$w(u o v)=min(val[u],val[v]) $ ,然后我们求一个最小瓶颈生成树就可以了,那么在树上我们就可以 (n^2) 的统计任意两点间路径最小值的平均数了,但这还是不行,那就继续考虑拆式子,考虑一条边会在什么时候向答案做出贡献,那就是在它是现在情况下最小的边时,我们发现这样每个边的贡献就是在模拟最小瓶颈生成树的生成过程,每条边会被连接的两个连通块统计上一次,直接上并查集就可以了

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	const int maxn = 2e5+5;
	int n,m,cnt,tot;
	int head[maxn],fa[maxn],siz[maxn];
	double v[maxn],ans;
	
	struct edge
	{
		int frm,to;
		double val;
	}e[maxn];
	
	bool cmp(edge a,edge b)
	{
		return a.val>b.val;
	}
	
	int find(int x)
	{
		return fa[x]==x?x:fa[x]=find(fa[x]);
	}
	
	void merge(int x,int y)
	{
		int fx=find(x);
		int fy=find(y);
		if(fx!=fy)
		{
			if(siz[fx]<siz[fy]) swap(fx,fy);
			siz[fx]+=siz[fy];
			fa[fy]=fx;
		}
	}
	
	void work()
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) scanf("%lf",&v[i]),fa[i]=i,siz[i]=1;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&e[i].frm,&e[i].to);
			e[i].val=min(v[e[i].frm],v[e[i].to]);
		}
		sort(e+1,e+m+1,cmp);
		for(int i=1;i<=m&&tot<n;i++)
		{
			int u=find(e[i].frm);
			int v=find(e[i].to);
			double w=e[i].val;
			if(u!=v)
			{
				tot++;
				ans+=w*siz[u]*siz[v];
				merge(u,v);
			}
		}
		printf("%.6f
",ans/n*2/(n-1));
	}

}

int main()
{
	zzc::work();
	return 0;
}

原文地址:https://www.cnblogs.com/youth518/p/13904555.html