#二分 #深度优先搜索
## 题目大意
给定一个 $n$ 个结点的数,现在要你断掉 $k$ 条边,使得所有联通块当中的最小大小最大。
## 思路
最小值最大,而且答案满足单调性,那么我们就可以使用二分求解,然后贪心地对可能的答案进行判断。不难想出,越在下面满足条件的地方进行切分,那么分出的联通块数量也是最大的,最后就看一看是否满足切分了 $k$ 刀即可。
注意一个特判,如果最后一块不够的话就少切一刀。
## 代码
```cpp
#include <iostream>
#include <vector>
using namespace std;
const int kMaxN = 1e5 + 5;
int t, n, m, k, cnt, ans;
vector<int> e[kMaxN];
int dfs(int x, int f) {
int sum = 1; // 可用的结点数量
for (int i : e[x]) { // 枚举所有儿子
if (i != f) {
sum += dfs(i, x); // 递归并加上儿子可用的结点
}
}
if (sum >= m) { // 如果可用结点数量够了
cnt += !f; // 在不是根结点的情况下断边
return 0; // 子树被单独分出来了
}
return sum; // 否则返回可用的结点
}
bool check() {
cnt = 0; // 清零
int res = dfs(1, 0); // 根结点的可用数量
res && res < m && cnt--; // 不够的话少切一条边
return cnt >= k; // 判断是否能够切 k 刀
}
int main() {
for (cin >> t; t; t--) {
cin >> n >> k;
fill(e + 1, e + n + 1, vector<int>());
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
for (int l = 1, r = n; l <= r; ) {
m = (l + r) >> 1;
if (check()) {
ans = m, l = m + 1;
} else {
r = m - 1;
}
}
cout << ans << '\n';
}
return 0;
}
```