赛道修建——二分答案

题目链接: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\n",ans);
    return 0;
}

 

posted @ 2020-08-17 15:10  zjy1412  阅读(168)  评论(0编辑  收藏  举报