【题解】Luogu P5021 赛道修建 贪心+二分
①最小值最大->二分答案
②贪心策略
从下向上匹配,先考虑树只有两层(一个根和一堆儿子) 显然想让他们中多出现赛道,我们把边权排序,从最小的边开始,用mid-w得到x,然后找比x略大或等于x的边,因为要使向上匹配的贡献最大,所以不能找比x长太多的边
处理完最下层后向上递归的同时将处理完的链的贡献向上传递
记f[i]为点i从下向上能走到的最长链长度,也就是向上的贡献
每次匹配成功即赛道数--,是否可行直接判断赛道数≤0
code
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 namespace gengyf{ 4 #define ll long long 5 const int mod=1e9+7; 6 const int maxn=1e5+10; 7 inline int read(){ 8 int x=0,f=1; 9 char c=getchar(); 10 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 11 while(c>='0'&&c<='9'){x=(x*10)+c-'0';c=getchar();} 12 return x*f; 13 } 14 int n,m,q[maxn],f[maxn],ans; 15 struct edge{ 16 int to,nxt,w; 17 }e[maxn*2]; 18 int head[maxn],cnt,sum,rt,tmp; 19 int v[maxn]; 20 inline void add(int from,int to,int w){ 21 e[++cnt].to=to;e[cnt].w=w;e[cnt].nxt=head[from];head[from]=cnt; 22 } 23 void dfs(int x,int from,int lim){ 24 for(int i=head[x];i;i=e[i].nxt){ 25 if(e[i].to!=from){ 26 dfs(e[i].to,x,lim); 27 } 28 } 29 int tail=0; 30 for(int i=head[x];i;i=e[i].nxt){ 31 if(e[i].to!=from){ 32 q[++tail]=f[e[i].to]+e[i].w; 33 } 34 } 35 sort(q+1,q+1+tail); 36 for(int i=tail;i>=1&&q[i]>=lim;i--){ 37 tail--;tmp--; 38 } 39 for(int i=1;i<=tail;i++){ 40 if(v[i]!=x){ 41 int l=i+1,r=tail,res=tail+1; 42 while(l<=r){ 43 int mid=(l+r)>>1; 44 if(q[i]+q[mid]>=lim){ 45 res=mid;r=mid-1; 46 } 47 else l=mid+1; 48 } 49 while(v[res]==x&&res<=tail)res++; 50 if(res<=tail){ 51 v[i]=v[res]=x;tmp--; 52 } 53 } 54 } 55 f[x]=0; 56 for(int i=tail;i>=1;i--){ 57 if(v[i]!=x){ 58 f[x]=q[i];break; 59 } 60 } 61 return ; 62 } 63 int main(){ 64 n=read();m=read(); 65 for(int i=1;i<n;i++){ 66 int u,v,w; 67 u=read();v=read();w=read(); 68 add(u,v,w);add(v,u,w);sum+=w; 69 } 70 rt=1; 71 int l=1,r=sum,mid; 72 while(l<=r){ 73 mid=(l+r)>>1; 74 tmp=m; 75 memset(v,0,sizeof(v)); 76 dfs(1,0,mid); 77 if(tmp<=0){ 78 l=mid+1;ans=mid; 79 } 80 else r=mid-1; 81 } 82 printf("%d",ans); 83 return 0; 84 } 85 } 86 signed main(){ 87 gengyf::main(); 88 return 0; 89 }