title
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;
}