码迷,mamicode.com
首页 > 其他好文 > 详细

[CF444E]DZY Loves Planting

时间:2017-12-09 13:15:28      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:algo   分享   ima   src   cpp   span   opera   return   pre   

题意:给一棵带边权的树,定义$g(x,y)$为$x\rightarrow y$路径上的最大边权,对于整数序列$p_{1\cdots n}$,定义$f(p)=\min\limits_{i=1}^ng(i,p_i)$,求$p_i$的最大值,还有限制:数字$j$在$p$中不能出现多于$x_j$次

这题......好像原出题人想到二分答案+网络流,被某神犇的并查集完虐了......

把边从小到大排序,扫一遍检查每条边能否成为答案,然后把这两个点合并(因为已合并的边都不大于当前扫到的边)

关键在于怎么检查,设$cnt_i$表示节点$i$已合并的点的$x_j$之和,$siz_i$表示节点$i$已合并的点数,$sum=\sum\limits_{j=1}^nx_j$

设当前扫到$(a,b)$这条边,我们要把$a$已合并的点连出去

技术分享图片

因为在($a$已合并的点和$b$已合并的点)中的边都不比$(a,b)$大,所以这样就能让比$(a,b)$小的边不在$p$中

因为所有$a$已合并的点都要向外连出去,所以可以连的条件自然就是$siz_a\leq sum-cnt_a$

所以我们用并查集维护$cnt,siz$边合并边检查,就搞定了

#include<stdio.h>
#include<algorithm>
using namespace std;
struct edge{
	int a,b,v;
}e[3010];
int fa[3010],siz[3010],cnt[3010],sum;
bool can;
bool operator<(edge a,edge b){
	return a.v<b.v;
}
int getfa(int x){
	if(fa[x]==x)return x;
	return fa[x]=getfa(fa[x]);
}
void merge(int x,int y){
	x=getfa(x);
	y=getfa(y);
	siz[x]+=siz[y];
	cnt[x]+=cnt[y];
	fa[y]=x;
	if(siz[x]>sum-cnt[x])can=0;
}
int main(){
	int n,i,ans;
	scanf("%d",&n);
	for(i=1;i<n;i++)scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].v);
	for(i=1;i<=n;i++){
		scanf("%d",cnt+i);
		sum+=cnt[i];
		siz[i]=1;
		fa[i]=i;
	}
	sort(e+1,e+n);
	ans=0;
	can=1;
	for(i=1;i<n;i++){
		if(can)ans=e[i].v;
		merge(e[i].a,e[i].b);
	}
	printf("%d",ans);
}

[CF444E]DZY Loves Planting

标签:algo   分享   ima   src   cpp   span   opera   return   pre   

原文地址:http://www.cnblogs.com/jefflyy/p/8011025.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!