H Tree Partition 二分+贪心
重现赛地址:
https://ac.nowcoder.com/acm/contest/4370
题意:
将一个树分成K棵子树,取每棵子树的和为分数,最小化分数的最大值。
基本思路:
最小化最大值,二分,二分每棵子树分数的最大值,然后每次贪心的取尽量多的节点构建子树,如果能够得到的子树数量小于等于K那么就说明最大值还能缩,并且最大值至少也要大于单个节点的最大值。
(特别注意在二分过程中的边界问题,并且附上几个万能二分模板)
参考代码:
#include <bits/stdc++.h>
using namespace std;
#define IO std::ios::sync_with_stdio(false)
#define int long long
#define INF 0x3f3f3f3f
const int maxn = 1e5 + 10;
int n,k;
vector<int> G[maxn];
int cost[maxn];
int mid = 0,sum = 0;
int memo[maxn];
int dfs(int s,int f){
priority_queue<int,vector<int>,less<>> pq;
for(auto it : G[s]){
if(it == f) continue;
cost[s] += dfs(it,s);
pq.push(cost[it]);
}
while (!pq.empty() && cost[s] > mid){
cost[s] -= pq.top();
pq.pop();
sum++;
}
while (!pq.empty()) pq.pop();
return cost[s];
}
signed main() {
IO;
int t;
cin >> t;
for (int cas = 1; cas <= t; cas++) {
cin >> n >> k;
for (int i = 1; i <= n; i++) G[i].clear();
for (int i = 0; i < n - 1; i++) {
int x, y;
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
int l = 0;
for (int i = 1; i <= n; i++) cin >> memo[i], l = max(l, memo[i]);
int r = 1e18;
int ans = 0;
while (l <= r) {
mid = (l + r) / 2;
for (int i = 1; i <= n; i++) cost[i] = memo[i];
sum = 1;
dfs(1, 0);
if (sum <= k) r = mid - 1,ans = mid;
else l = mid + 1;
}
cout << "Case #" << cas << ": " << ans << endl;
}
return 0;
}
整数的万能二分模板:
int binary(int n)
{
int l = 1, r = maxn, ans = 0;
while(l <= r)
{
int mid = (l + r) >> 1;
if(c[mid] > a[n]) ans = mid, l = mid + 1; //判断条件与ans记录位置因题而异
else r = mid - 1;
}
return ans;
}
输出小数的二分模板
double binary(){
double lb = 0 , ub = INF;
//重复循环直到解的范围足够小;
for(int i = 0 ; i < 100 ; i++){
double mid = (lb + ub) / 2;
if(C(mid)) lb = mid;
else ub = mid;
}
return floor(ub*100)/100;//保留两位;
}