给出一棵树, 在树上找出 条路径, 路径之间不互相覆盖, 求出 最小边最大 的方案
1. 主体思路
“最小边最大” 直接可以想到 二分答案 :
第一步, 二分 最小边权: ,
第二步, :
设当前节点为 , 其子节点集合为 , 为了凑成长度为 的边, 有两种方法
- 到 节点 合法路径 大于等于
- 到 节点 合法路径和 的长度 大于等于
上两种情况能够得到的合法路径 统统采用 !!!,
理由如下
假设不采用 路径, 在往 上面的节点采用, 则会浪费 与其上面节点间的边权,
假设不采用 路径, 在往 上面的节点采用,
- 此时可能因为上面的边权太小, 导致这 条路径皆作废 ;
- 上面的边权可以与两条边中较大的路径匹配, 另一条路径作废.
: 与其让另一条路径作废, 不如省下 上的某条边.
2. 操作细节
接下来说明怎么正确地组合 集合中传上来的最长路径:
-
首先对于 的路径, 直接 ++;
-
然后选边时尽量选小的, 因为到后面还要将尽量大的值 往上传,
于是每次取出最小的边 , 在 集合中 二分查找 ,- 若找到则组合起来, ++, 否则直接 往上传 最大值.
- 否则迭代器 ++
选边过程可以使用 维护 .
注意二分的右边界需要设为树的直径, 无脑设 会
#include<bits/stdc++.h>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ c = getchar(); flag = -1; break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 50005;
int N;
int M;
int res;
int Ans;
int Mid;
int num0;
int head[maxn];
struct Edge{ int nxt, to, w; } edge[maxn<<1];
void Add(int from, int to, int w){
edge[++ num0] = (Edge){ head[from], to, w };
head[from] = num0;
}
int DFS(int k, int fa){
std::multiset <int> Q;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
int tmp = DFS(to, k);
if(edge[i].w + tmp >= Mid) res ++;
else Q.insert(tmp+edge[i].w);
}
std::multiset<int>::iterator it, pos;
if(!Q.empty()){
it = Q.begin();
while(Q.size() >= 2 && it != Q.end()){
pos = Q.lower_bound(Mid - *it);
if(pos == it) pos ++;
if(pos != Q.end()){
Q.erase(pos), Q.erase(it ++);
res ++;
} else it ++;
}
return Q.size()?*(--Q.end()):0;
}
return 0;
}
int max_d, max_id;
void DFS_1(int k, int fa, int sum){
if(max_d < sum) max_d = sum, max_id = k;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
DFS_1(to, k, sum+edge[i].w);
}
}
int main(){
// freopen("road.in", "r", stdin);
// freopen("road.out", "w", stdout);
N = read(), M = read();
for(reg int i = 1; i < N; i ++){
int u = read(), v = read(), w = read();
Add(u, v, w), Add(v, u, w);
}
DFS_1(1, 0, 0), max_d = 0;
DFS_1(max_id, 0, 0);
int l = 1, r = max_d;
while(l <= r){
Mid = l+r >> 1;
res = 0, DFS(1, 0);
if(res >= M) Ans = std::max(Ans, Mid), l = Mid+1;
else r = Mid-1;
}
printf("%d
", Ans);
return 0;
}