洛谷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\n",ans); return 0; }