LUOGU 5021: [Noip2018]赛道修建 二分答案+树形Dp

title

LUOGU 5021

Description

C 城将要举办一系列的赛车比赛。在比赛前,需要在城内修建 \(m\) 条赛道。

C 城一共有 \(n\) 个路口,这些路口编号为 \(1,2,…,n\) ,有 \(n-1\) 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第 \(i\) 条道路连接的两个路口编号为 \(a_i\)\(b_i\),该道路的长度为 \(l_i\)。借助这 \(n-1\) 条道路,从任何一个路口出发都能到达其他所有的路口。

一条赛道是一组互不相同的道路 \(e_1,e_2,…,e_k\) ,满足可以从某个路口出发,依次经过 道路 \(e_1,e_2,…,e_k\) (每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。

目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的 \(m\) 条赛道中长度最小的赛道长度最大(即 \(m\) 条赛道中最短赛道的长度尽可能大)

analysis

二分答案专治这种 “使最小的最大” 类型题目,那么我们考虑怎么 \(check\) 吧。

我们设定状态 \(f_x\) 表示在 \(x\) 的子树中未被选过的权值最大的路径权值是多少。

那么对于子节点 \(y\) ,如果有 \(f_y+edge_{x,y}>=mid\) ,就可以选择,

否则再选一条路径和它拼在一起即可。

这个过程开个 \(multiset\) 可以较简单地做。

时间复杂度 \(O(n\log^2n)\)\(multiset\) 常数有点大,需要开 \(O2\))。

code

#include<bits/stdc++.h>

const int maxn=5e4+10;

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x,char str)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++=str;
    }
}

using IO::read;
using IO::write;

int ver[maxn<<1],edge[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y,int z)
{
    ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
}

std::multiset<int>s;
std::multiset<int>::iterator it;
int f[maxn],mid,cnt, Stack[maxn],top;
inline void dfs(int x,int fa)
{
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (y==fa) continue;
        dfs(y,x);
    }
    top=0;
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (y==fa) continue;
        f[y]+=edge[i];
        if (f[y]>=mid) ++cnt;
        else Stack[++top]=f[y];
    }
    std::sort(&Stack[1],&Stack[top+1]);
    s.clear();
    for (int i=1; i<=top; ++i)
    {
        it=s.lower_bound(mid-Stack[i]);
        if (it!=s.end()) s.erase(it), ++cnt;
        else s.insert(Stack[i]);
    }
    f[x]=s.size() ? *s.rbegin() : 0;
}

int main()
{
    int n,m;read(n);read(m);
    int l=0, r=0;
    for (int i=1,x,y,z; i<n; ++i) read(x),read(y),read(z),add(x,y,z),add(y,x,z),r+=z;
    int ans=(r=r/m);
    while (l<=r)
    {
        mid=(l+r)>>1, cnt=0;
        dfs(1,0);
        if (cnt>=m) ans=mid,l=mid+1;
        else r=mid-1;
    }
    write(ans,'\n');
    IO::flush();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/G-hsm/p/11431143.html
今日推荐