CF444E DZY Loves Planting 并查集

题意:

给定一颗大小为\(n\)的树,定义\(g(i,j)\)表示树上\(i\)\(j\)点路径上,最大的一个边权,现在要求你给树上每一个点\(i\)选择一个点\(j\)进行配对,每一个点最多被选\(x_i\)次,使得\(min\ g(i,j)\)最大

范围&性质: \(1\le n\le 3000,1\le x_i\le n\)

分析:

暴力做法:

二分答案,check()的时候通过带权二分图匹配的方法进行调整,复杂度为\(O(n^3log)\)想到了,不过因为码量不小考场上没写出来

正解:

对所有边进行排序,从小到大枚举,对于每一个边所连接的两个连通块如果不得不经过这条边时,答案即为这条边的边权,那么对于什么情况下存在点不得不经过这条边,换句话说就是无法通过配对使得联通块内每一个点都有不经过这条边的选择,转换成代码的话,我们记\(sum\)为连通块内部\(x_i\)的和,\(siz\)为连通块的大小,当\(siz>\sum_{i=1}^n x_i-sum\)不得不经过这条边,复杂度\(O(nlog)\)

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	const int maxn = 3005;
	int n,sum;
	int fa[maxn],siz[maxn],x[maxn];
	
	struct edge
	{
		int frm,to,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 work()
	{
		scanf("%d",&n);
		for(int i=1;i<n;i++)
		{
			scanf("%d%d%d",&e[i].frm,&e[i].to,&e[i].val);
		}
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&x[i]);
			sum+=x[i];
		}
		for(int i=1;i<=n;i++)
		{
			fa[i]=i;siz[i]=1;
		}
		sort(e+1,e+n,cmp);
		for(int i=1;i<=n-1;i++)
		{
			int u=e[i].frm;
			int v=e[i].to;
			int fx=find(u);
			int fy=find(v);
			fa[fy]=fx;
			siz[fx]+=siz[fy];
			x[fx]+=x[fy];
			if(siz[fx]>sum-x[fx])
			{
				printf("%d\n",e[i].val);
				return ;
			}
		}
		printf("%d\n",e[n-1].val);
	}
	
}


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

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