https://www.luogu.com.cn/problem/P5021
https://loj.ac/problem/2952
让你在一个带权树上选若干条简单路径,使得每条没有公共边,且最短的路径最长
最短路径最长,可以考虑二分,二分每一条路径都必须大于某个值
先考虑在一个子树内如何选才最优,对于子树的每一个儿子
- 如果从这个儿子向下延伸若干条边(在加上它和这个儿子的那条边)组成的路径,已经比这个值大,那它自己构成一个简单路径
- 把剩下的每个儿子延伸若干边(加上它们与父亲间的边),的总长度,按从小到大排序,枚举每个未被使用的儿子,再二分看能和他组成一条路径(总长度大于当前二分的值)的另一个儿子的长度最短是多少,就是组成以这个子树的根为 lca,分别向这两个儿子的方向延伸所得的一个简单路径
- 在考虑还能从这个点往上延伸组成简单路径,但显然它与他父亲间只有一个边,所以只能延伸一条,就从没有被使用的儿子里选一个长度最大的网上延伸就行了(这也体现了为什么要从小到大排序,因为如果从大到小枚举的话,会导致有的儿子可能向上延伸更优,结果却被选成了和其他儿子组成路径)。那么这个儿子的长度加上他与他父亲边的长度,就会在回朔它父亲时,被考虑成刚才描述的“每个儿子向下延伸若干条边的长度”
所以每个二分的 check 实际就是一个 dfs
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN puts("")
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define N 50005
#define M 100005
struct graph{
int fir[N],nex[M],to[M],w[M],tot;
inline void add(int u,int v,int W){
to[++tot]=v;w[tot]=W;
nex[tot]=fir[u];fir[u]=tot;
}
}G;
int n,m;
int que[N],right,used[N];
int f[N];
inline int find(int x,reg int l,reg int r){
if(l>r) return r+1;
if(que[r]<x) return r+1;
if(que[l]>=x) return l;
reg int mid,ans;
while(l<=r){
mid=(l+r)>>1;
if(que[mid]>=x) r=mid-1,ans=mid;
else l=mid+1;
}
return ans;
}
int res;
void dfs(reg int u,int fa,int min){
for(reg int v,i=G.fir[u];i;i=G.nex[i]){
v=G.to[i];
if(v==fa) continue;
dfs(v,u,min);
}
right=0;
for(reg int v,i=G.fir[u];i;i=G.nex[i]){
v=G.to[i];
if(v==fa) continue;
que[++right]=f[v]+G.w[i];
}
std::sort(que+1,que+1+right);
while(right&&que[right]>=min) res--,right--;
for(reg int pos,i=1;i<=right;i++)if(used[i]^u){
pos=find(min-que[i],i+1,right);
while(used[pos]==u&&pos<=right) pos++;
if(pos<=right) used[i]=used[pos]=u,res--;
}
f[u]=0;
for(reg int i=right;i;i--)if(used[i]^u){
f[u]=que[i];break;
}
}
int main(){
std::freopen("track.in","r",stdin);
std::freopen("track.out","w",stdout);
n=read();m=read();
for(reg int u,v,w,i=1;i<n;i++){
u=read();v=read();w=read();
G.add(u,v,w);G.add(v,u,w);
}
reg int l=1,r=5e8,mid,ans;
while(l<=r){
mid=(l+r)>>1;
res=m;
if(mid==38744){
int aa=1;
aa++;
}
std::memset(used,0,sizeof used);
dfs(1,1,mid);
if(res<=0) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d",ans);
return 0;
}