题目大意:给定一棵$n$个点的树,将叶子节点分为数个集合,使集合里点对最长距离不超过$k$,求最少集合数。
题解:贪心,发现将叶子节点分成集合等于把节点划分集合,答案是一样的。因为一定有一个点,到非叶子节点$p$的儿子的距离比到$p$远。然后发现一个集合一定是连续的(或者连续的一定最优),不然不完全可以把中间连起来,且合法。
$dfs$,如果一个点的最长两个点长度和大于$k$就加一个集合
注意要用非叶子节点当根,最后答案要加一
卡点:没用非叶子节点当根
C++ Code:
#include <cstdio>
#include <vector>
#include <algorithm>
#define maxn 1000010
int head[maxn], cnt;
struct Edge {
int to, nxt;
} e[maxn << 1];
inline void add(int a, int b) {
e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
}
int n, k, ans;
int ind[maxn], fa[maxn], M[maxn];
void dfs(int u) {
if (ind[u] < 2) return ;
std::vector<int> E;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v != fa[u]) {
fa[v] = u;
dfs(v);
E.push_back(M[v] + 1);
}
}
std::sort(E.begin(), E.end());
int now = E.size() - 1;
for (; now; now--) if (E[now] + E[now - 1] > k) ans++;
else break;
M[u] = E[now];
}
int main() {
scanf("%d%d", &n, &k);
int rt = 1;
for (int i = 1, a, b; i < n; i++) {
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
ind[a]++, ind[b]++;
if (ind[a] != 1) rt = a;
if (ind[b] != 1) rt = b;
}
dfs(rt);
printf("%d\n", ans + 1);
return 0;
}