suxxsfe

一言(ヒトコト)

P5021 赛道修建

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;
}
posted @ 2020-10-02 21:06  suxxsfe  阅读(135)  评论(0编辑  收藏  举报