题目链接:https://www.luogu.com.cn/problem/P5021
分析:
题目大意就不讲了。此题很多人说一眼就知道是二分,那就当我也这么认为吧。此题思考的关键在于不能重边。先设二分的值为k,我们思考一个状态:此时搜到一个点,暂时不考虑下面回溯上来的值,当该值加上其对应的边权大于等于k,则直接统计到答案里,剩余的不满k的值先存起来。我们知道剩余的这些边中还是会存在满足大于等于k的情况,只要任意两条加在一起大于等于k就可以计入答案。(记住这里要用贪心,即对于每条边选择可行方案里最小的)接下来同样按着贪心,选择一条剩余边里最大的一条回溯上去,该值即上文回溯上来的值。看不懂的看代码食用更佳哦!
补充:代码里用到了set库里的multiset,这里使用能更佳方便地实现代码,不会的建议了解一下。
代码:
#include<iostream> #include<cstdio> #include<vector> #include<cstring> #include<set> using namespace std; #define int long long #define R register inline int read(){ int a=0,b=1;char c=getchar(); while(!isdigit(c)){if(c=='-')b=-1;c=getchar();} while(isdigit(c)){a=a*10+c-'0';c=getchar();} return a*b; } multiset<int>::iterator it; const int N=5e4+50,M=2e5+50; int n,m,tot,cnt,h[N],ver[M],nx[M],ed[M],ave,l,r,ans; void add(int u,int v,int z){ ver[++tot]=v;ed[tot]=z; nx[tot]=h[u];h[u]=tot; } int dfs(int x,int fa,int kkk){ multiset<int>s; for(R int i=h[x],dis;i;i=nx[i]){ int v=ver[i],z=ed[i]; if(v==fa)continue; dis=z+dfs(v,x,kkk); if(dis>=kkk)cnt++; else s.insert(dis); } int maxn=0; while(s.size()){ int p=*s.begin(); s.erase(s.begin()); it=s.lower_bound(kkk-p); if(it!=s.end()){ cnt++; s.erase(it); } else{ maxn=max(maxn,p); } } return maxn; } bool check(int kkk){ cnt=0; dfs(1,0,kkk); if(cnt>=m)return true; else return false; } signed main(){ n=read();m=read(); int u,v,z; for(R int i=1;i<n;i++){ u=read();v=read();z=read(); add(u,v,z);add(v,u,z); ave+=z; } ave/=m; l=1,r=ave; int mid; while(l<=r){ mid=(l+r)>>1; if(check(mid))ans=mid,l=mid+1; else r=mid-1; } printf("%lld ",ans); return 0; }