鱼香rose'Blog

codeforces 936 C

\(\Huge{codeforces~936(Div3)~C.Tree Cutting}\)

题目链接:Problem - C - Codeforces

题意

给出一颗有\(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;
}
posted @ 2026-01-15 21:51  鱼香_rose  阅读(0)  评论(0)    收藏  举报