• 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;
    }
    
  • 相关阅读:
    JZOJ 2020.10.6 提高B组反思
    【NOIP2011模拟11.1】钓鱼
    JZOJ【NOIP2012模拟8.9】2020.10.5 T1
    ⑫linux基础命令 过滤 grep
    ⑪linux基础命令 tail
    ⑩linux基础命令 head
    ⑨linux基础命令 cat
    ⑧linux基础命令 rm
    ⑦linux基础命令 mv
    ⑥linux基础命令 cp
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13762727.html
Copyright © 2020-2023  润新知