洛谷P5021 赛道修建

传送门

二分所求的最大值,树形dp判断能否凑够m个

每条赛道的贡献都是1,贪心地让子树中尽可能凑出赛道,将剩下的最长链传上去

用multiset储存所有子节点传上来的链,从小到大尝试让每条链进行匹配,二分multiset中最小的满足匹配的值,将它们删去。这样可以保证进行匹配的尽量多,并且最后传上去的尽量大。

#include<iostream>
#include<cstdio>
#include<set>
#include<algorithm>
using namespace std;
int n,m,sum,ans,num,a[50010];
int ver[100010],Next[100010],head[50010],edge[100010],tot;
multiset<int>s[50010];
multiset<int>::iterator it,it1;
void add(int x,int y,int z){
    ver[++tot]=y;
    Next[tot]=head[x];
    head[x]=tot;
    edge[tot]=z;
}
int dfs(int x,int fa,int val,int edg){
    int maxx=0;
    s[x].clear();
    for(int i=head[x];i;i=Next[i]){
        int y=ver[i];
        if(y==fa)continue;
        int w=dfs(y,x,val,edge[i]);
        if(w>=val)num++;
        else s[x].insert(w);
    }
    if(!s[x].size())return edg;
    while(s[x].size()){
        it=s[x].begin();
        int w=*it;
        s[x].erase(it);
        it1=s[x].lower_bound(val-w);
        if(it1!=s[x].end()){
            num++;
            s[x].erase(it1);
        }
        else{
            maxx=max(maxx,w);
        }
    }
    return maxx+edg;
}
int check(int x){
    num=0;
    dfs(1,0,x,0);
    return num>=m;
}
int main()
{
//    freopen("1.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1,x,y,z;i<n;i++){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,z);
        sum+=z;
    }
    int l=1,r=sum/m;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid)){
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%d
",ans);
    return 0;
} 
View Code
原文地址:https://www.cnblogs.com/chloris/p/11855835.html