[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();
}
}