P5021 赛道修建 (NOIP2018)

传送门

考场上把暴力都打满了,结果文件输入输出写错了....

当时时间很充裕,如果认真想想正解是可以想出来的..

问你 长度最小的赛道长度的最大值

显然二分答案

考虑如何判断是否可行

显然对于一个节点,它最多只能向父亲传一条路径长度

那么其它路径的合并只能在子树间进行

贪心一波,如果一段路径在子树就可以合并出合法长度

那么直接合并一定是不会比传给父亲再考虑合并更劣的

子树都合并完后,剩下的肯定传最长的长度给父亲

考虑在子树内如何贪心地进行最优合并

显然最小的边去找最小的能使它合法的边合并

(注意不是最大的边去找最小的能使它合法的边合并)

那么用一个multiset存一下当前节点的各个边长

然后就按前面的方法一个个合并就好了

注意和并时的一些细节和二分的上界,(因为multiset常数大,所以要把上界设为总长除赛道数)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int N=1e5+7,INF=2e9+7;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
int fir[N],from[N<<1],to[N<<1],val[N<<1],cntt;
inline void add(int &a,int &b,int &c)
{
    from[++cntt]=fir[a];
    fir[a]=cntt; to[cntt]=b; val[cntt]=c;
}
int n,m,mid,cnt;
int f[N];
multiset <int> S;
multiset <int>::iterator it,itt,pit;
void dfs(int x,int fa)
{
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(fa==v) continue;
        dfs(v,x);//先把每个节点遍历一波再从后往前更新会比较方便(不然要开一堆set)
    }
    S.clear();//记得清空
    for(int i=fir[x];i;i=from[i])
    {
        int &v=to[i]; if(fa==v) continue;
        if(f[v]+val[i]>=mid) cnt++;//只考虑小于mid的长度的合并(大于的不用合并都有贡献)
        else S.insert(f[v]+val[i]);//注意把儿子传的边长加上父子间的边长
    }
    it=S.begin();
    while(it!=S.end())
    {
        itt=S.lower_bound(mid-(*it));
        if(itt==it) itt++;//注意细节
        if(itt!=S.end()) cnt++,S.erase(itt),pit=it,it++,S.erase(pit);
        else it++;
    }
    f[x]=0;/*记得先清空f*/ it=S.begin(); if(S.end()!=it) it=S.end(),it--,f[x]=*it;//注意必须有边传才更新f
}

inline int judge()
{
    cnt=0; dfs(1,1);
    return cnt>=m;
}

int main()
{
    int a,b,c,s=0;
    n=read(); m=read();
    for(int i=1;i<n;i++)
    {
        a=read(); b=read(); c=read();
        add(a,b,c); add(b,a,c); s+=c;
    }
    int L=0,R=s/m;
    while(R-L>1)
    {
        mid=L+R>>1;
        if(judge()) L=mid; else R=mid;
    }
    mid=R;
    printf("%d",judge() ? R : L);
    return 0;
}

 

posted @ 2018-12-05 21:43  LLTYYC  阅读(262)  评论(0编辑  收藏  举报