codeforces 936 C
\(\Huge{codeforces~936(Div3)~C.Tree Cutting}\)
题意
给出一颗有\(n\)个节点的树,然后给出\(n-1\)条边的情况。然后给出一个\(k\),要求删掉树中的\(k\)条边,要求删掉边后的节点最少的子树的节点数最多。\((1\le k < n \le 10^5)\)
思路
根据题意,题目要求节点最少的子树的节点数最多,那么当为结果\(res\)时,\(res-1,res-2…\)也可以通过删边得到。所以结果满足单调性,考虑使用二分。
由于是树形结构,我们可以假设在每个点保存其后代节点的个数,表示以当前点作为根节点时子树大小。
那么如果结果为res时,我们首先应该将节点个数大于等于res的最小值的子树剪掉。
但是这个思路实现过程中,如果通过最后剪了\(k\)次之后最小子树是否为\(res\)来判断,比较难以实现。
因此,考虑当只有子树符合要求时剪去,否则不剪,若最后剪的次数大于等于\(k\),则当前\(res\)满足要求,二分中间值应缩小。
标程
int n, k;
vector<int> a[N];
bool check(int mid) {
int t = 0;
auto dfs = [&](auto self, int x, int y) -> int {
int sum = 1;
for(auto i : a[x]) {
if(i == y) continue;
sum += self(self, i, x);//从叶子节点向上返回节点个数
}
if(sum >= mid && x != y) t ++, sum = 0;//如果个数符合就将其剪去,可以用向上返回0实现
return sum;
};
int x = dfs(dfs, 1, 1);
if(t > k || (t == k && x >= mid)) return true;
else return false;
}
void Solved() {
cin >> n >> k;
for(int i = 1; i <= n; i ++ ) a[i].clear();//init
for(int i = 1; i < n; i ++ ) {
int x, y; cin >> x >> y;
a[x].push_back(y); a[y].push_back(x);
}
int l = 1, r = n / (k + 1) + 1, mid;
while(r - l > 1) {
mid = l + r >> 1;
if(check(mid)) l = mid;
else r = mid;
}
cout << l << endl;
}

浙公网安备 33010602011771号