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;
    } 
    
  • 相关阅读:
    hdu 4614 线段树 二分
    cf 1066d 思维 二分
    lca 最大生成树 逆向思维 2018 徐州赛区网络预赛j
    rmq学习
    hdu 5692 dfs序 线段树
    dfs序介绍
    poj 3321 dfs序 树状数组 前向星
    cf 1060d 思维贪心
    【PAT甲级】1126 Eulerian Path (25分)
    【PAT甲级】1125 Chain the Ropes (25分)
  • 原文地址:https://www.cnblogs.com/h-lka/p/13721716.html
Copyright © 2011-2022 走看看