传送门
C 城将要举办一系列的赛车比赛。在比赛前,需要在城内修建 m m m 条赛道。
C 城一共有 n n n 个路口,这些路口编号为 1 , 2 , … , n 1,2,…,n 1,2,…,n,有 n − 1 n-1 n−1 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第 i i i 条道路连接的两个路口编号为 a i a_i ai 和 b i b_i bi,该道路的长度为 l i l_i li。借助这 n − 1 n-1 n−1 条道路,从任何一个路口出发都能到达其他所有的路口。
一条赛道是一组互不相同的道路 e 1 , e 2 , … , e k e_1,e_2,…,e_k e1,e2,…,ek,满足可以从某个路口出发,依次经过道路 e 1 , e 2 , … , e k e_1,e_2,…,e_k e1,e2,…,ek(每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。
目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的 m m m 条赛道中长度最小的赛道长度最大(即 m m m 条赛道中最短赛道的长度尽可能大)。
数据范围: 2 ≤ n ≤ 50000 2≤n≤50000 2≤n≤50000, 1 ≤ m ≤ n − 1 1 ≤ m ≤ n-1 1≤m≤n−1, 1 ≤ l i ≤ 10000 1 ≤ l_i ≤ 10000 1≤li≤10000。
这道题二分应该很显然,如何检验答案?
考虑贪心。假设当前二分到的值为 k k k。
下文中的 “ “ “匹配 ” ” ”表示两条长度 < k <k <k 的链拼在一起后 ≥ k \geq k ≥k。
对于点 u u u,我们遍历它的儿子 v v v,每个 v v v 会传回来一个 v a l val val,表示 v v v 中匹配结束后剩下的最长的边,再加上 w ( u , v ) w(u,v) w(u,v)。
那么当前合法的情况就两种:
对于第一种,直接 a n s + + ans++ ans++ 就可以。
对于第二种,我们就要匹配,我们按照 v a l val val 从小到大排序,对于每一个 v a l val val,找到第一个 ≥ k − v a l \geq k-val ≥k−val 的链,把它们拼到一起,然后 a n s + + ans++ ans++。这一步用 multiset 维护就行。
注意最后求一下直径作为二分的上界,防止被菊花图卡掉。
#include
#include
#include
#include
#define N 100005
using namespace std;
int n,m,t,cnt,l=1,r=0;
int first[N],v[N],w[N],nxt[N];
multiset<int>S;
multiset<int>::iterator it;
void add(int x,int y,int z){
nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;
}
int f1[N],f2[N];
void dfs(int x,int fa){
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(to==fa) continue;
dfs(to,x);
if(f1[x]<f1[to]+w[i]){
f2[x]=f1[x];
f1[x]=f1[to]+w[i];
}
else if(f2[x]<f1[to]+w[i])
f2[x]=f1[to]+w[i];
r=max(r,f1[x]+f2[x]);
}
}
int f[N],tmp[N];
void dp(int x,int fa,int k){
for(int i=first[x];i;i=nxt[i])
if(v[i]!=fa) dp(v[i],x,k);
int top=0;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(to==fa) continue;
f[to]+=w[i];
(f[to]>=k)?(++cnt):(tmp[++top]=f[to]);
}
sort(tmp+1,tmp+top+1),S.clear();
for(int i=1;i<=top;++i){
it=S.lower_bound(k-tmp[i]);
if(it!=S.end()) S.erase(it),cnt++;
else S.insert(tmp[i]);
}
f[x]=S.size()?*S.rbegin():0;
}
bool check(int mid){
cnt=0,dp(1,0,mid);
return cnt>=m;
}
int main(){
int x,y,z;
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
dfs(1,0);
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}