zoukankan      html  css  js  c++  java
  • 【题解】NOIP2018 赛道修建

    题目戳我

    ( ext{Solution:})

    根据题目信息简化题意,是让你在树上找出(m)条路径使得路径长度最小值最大。

    看到题第一感先二分一个答案,问题转化为如何选择一些路径使得它们最小值都大于当前二分的答案。

    观察一个节点的子树,必然会带着若干链。它们有些合法,有些需要拼凑成一条合法路径。

    于是,我们令(val[x])表示这条路径以(x)结尾的长度。将(x)所连接的所有路径提取出来。

    贪心地,我们显然要使得(x)所连的这一条路径最大。所以在给其他路径拼凑的时候要用尽量小的链来凑出合法路径。

    于是,我们对所有提取出的路径排序并二分,找到和它可以匹配的最优路径,并标记。

    对于二分到的点,我们可以向后枚举直到一个未被标记的点。

    最后复杂度是(O(nlog^2 n).)注意(vis)数组用当前点做编号可以免去清空。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e5+10;
    const int inf=(1<<30);
    int n,m,tot=1,head[MAXN],rt,Siz[50001],mson[50001],M;
    int val[50001],up,vis[100010],q[100010];
    struct E{int nxt,to,dis;}e[MAXN];
    inline void add(int x,int y,int w){
    	e[++tot].to=y;e[tot].nxt=head[x];
    	e[tot].dis=w;head[x]=tot;
    }
    int Gr(int x,int fa){
    	int sum1=0,sum2=0;
    	Siz[x]=1;mson[x]=0;
    	for(int i=head[x];i;i=e[i].nxt){
    		int j=e[i].to;
    		if(j==fa)continue;
    		sum2=max(sum2,Gr(j,x)+e[i].dis);Siz[x]+=Siz[j];
    		if(Siz[j]>mson[x])mson[x]=Siz[j];
    		if(sum1<sum2)swap(sum1,sum2);
    	}
    	if(n-Siz[x]>mson[x])mson[x]=n-Siz[x];
    	if(M>mson[x])M=mson[x],rt=x;
    	up=max(up,sum1+sum2);
    	return sum1;
    }
    inline bool checkedge(int v1,int v2,int ck){return (v1+v2)>=ck;}
    int dfs(int x,int fa,int v){
    	int cnt=0,ans=0;
    	if(ans>=m)return ans;
    	for(int i=head[x];i;i=e[i].nxt){
    		int j=e[i].to;
    		if(j==fa)continue;
    		ans+=dfs(j,x,v);
    	}
    	for(int i=head[x];i;i=e[i].nxt){
    		int j=e[i].to;
    		if(j==fa)continue;
    		if(val[j]+e[i].dis>=v){
    			ans++;
    			continue;
    		}
    		q[++cnt]=e[i].dis+val[j];
    	}
    	sort(q+1,q+cnt+1);
    	for(int i=1;i<=cnt;++i){
    		if(vis[i]==x)continue;
    		int L=i+1,R=cnt,AA=0;
    		while(L<=R){
    			int mid=L+R>>1;
    			if(checkedge(q[i],q[mid],v))AA=mid,R=mid-1;
    			else L=mid+1;
    		}
    		while(vis[AA]==x&&AA<=cnt)AA++;
    		if(AA>cnt)continue;
    		if(vis[AA]==x)continue;
    		if(AA&&checkedge(q[i],q[AA],v)){
    			vis[AA]=x;vis[i]=x;
    			ans++;continue;
    		}
    	}
    	for(int i=cnt;i>=1;--i){
    		if(vis[i]==x)continue;
    		val[x]=q[i];break;
    	}
    	return ans;
    }
    bool check(int x){
    	for(int i=1;i<=n;++i)val[i]=0,vis[i]=vis[i+n]=-1;
    	int A=dfs(rt,0,x);
    	return A>=m;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	int l=1,r;
    	for(int i=1;i<n;++i){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z);add(y,x,z);
    	}
    	M=inf;Gr(1,0);r=up;
    	int ans=0;
    	while(l<=r){
    		int mid=l+r>>1;
    		if(check(mid))l=mid+1,ans=mid;
    		else r=mid-1;
    	}
    	printf("%d
    ",ans);
    	return 0;
    } 
    
  • 相关阅读:
    【转】属性与字段的区别
    学习C/C++的经验谈(转)
    [C++语法] 关键字typedef用法(转)
    让我们习惯在底层用C++宏生成代码 (转)
    C/C++笔试题 (二)【转】
    C/C++笔试题 (三)【转】
    C语言 printf格式控制符 完全解析
    C/C++笔试题 (一)【转】
    C++内存管理详解(转)
    【转】 Source Insight设置
  • 原文地址:https://www.cnblogs.com/h-lka/p/13721716.html
Copyright © 2011-2022 走看看