标签: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