标签: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);
}
标签:algo 分享 ima src cpp span opera return pre
原文地址:http://www.cnblogs.com/jefflyy/p/8011025.html