[ICPC2019上海H] Tree Partition - 二分,贪心


[ICPC2019上海H] Tree Partition - 二分,贪心

Description

给定一颗 n 个节点的树,每个点带点权,让你分成 k 份,使得连通块中最大的点权和最小,输出这个最小值

Solution

二分答案转化为判定问题,会有一个组内和上限 mid,我们 DFS 整棵树,每个子树在内部划分,剩下来的一点返回给父亲让父亲处理

父亲将所有孩子传给他的剩余量中,挑出较小的几个加到自己身上(留着待会向上传),保证这部分的和不超过 mid,其它的,只能一人分配一个新组

#include 
using namespace std;

#define int long long

const int N = 2e5 + 5;

void clear(int *a, int n)
{
    for (int i = 0; i <= n; i++)
        a[i] = 0;
}

int n, k;
vector g[N];
int w[N];
int ans, lim;

int dfs(int p, int from)
{
    int sum = w[p];
    vector vec;
    for (int q : g[p])
    {
        if (q == from)
            continue;
        int res = dfs(q, p);
        vec.push_back(res);
        if (res > lim)
        {
            ans = 1e9;
        }
    }
    sort(vec.begin(), vec.end());
    int c = 0;
    for (int i = 0; i < vec.size(); i++)
        if (sum + vec[i] <= lim)
            sum += vec[i], ++c;
    ans += vec.size() - c;
    return sum;
}

bool check(int mid)
{
    ans = 0;
    lim = mid;
    int res = dfs(1, 0);
    if (res > 0)
        ++ans;
    if (res > lim)
        return false;
    return ans <= k;
}

void solve()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
        g[i].clear();
    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    for (int i = 1; i <= n; i++)
        cin >> w[i];

    int l = 1, r = 1e18;
    while (l < r)
    {
        int mid = (l + r) / 2;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << l << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    int t;
    cin >> t;
    for (int i = 1; i <= t; i++)
    {
        cout << "Case #" << i << ": ";
        solve();
    }
}

相关