题目链接:2019 ICPC Asia Shanghai - Tree Partition
首先权值最大的块,最小。我们可以想到二分。
然后看是否能够满足。我们对于每个点,假设子树都是小于当前二分的值mid,对于当前点,如果把全部子树加上都是合法的,那么肯定直接加上即可。
如果全部加上不合法,肯定就需要丢弃一些子树。肯定贪心丢掉最大的子树,直到合法。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int n,k,a[N],flag,cnt,sum[N];
vector<int> g[N];
inline void add(int a,int b){g[a].push_back(b),g[b].push_back(a);}
inline void init(){
for(int i=1;i<=n;i++) g[i].clear(),sum[i]=0;
}
void dfs(int x,int fa,int mid){
sum[x]=a[x];
if(sum[x]>mid||flag){flag=1; return ;}
for(auto to:g[x]){
if(to==fa) continue;
dfs(to,x,mid); sum[x]+=sum[to];
}
if(sum[x]>mid){
vector<int> v;
for(auto to:g[x]) if(to!=fa) v.push_back(sum[to]);
sort(v.begin(),v.end());
while(sum[x]>mid){
cnt++; sum[x]-=v.back(); v.pop_back();
}
}
if(cnt>k-1) flag=1;
}
inline int check(int mid){
flag=cnt=0; dfs(1,1,mid);
return !flag;
}
inline int solve(){
cin>>n>>k; init();
for(int i=1,a,b;i<n;i++) scanf("%lld %lld",&a,&b),add(a,b);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
int l=0,r=1e14;
while(l<r){
int mid=l+r>>1LL;
if(check(mid)) r=mid;
else l=mid+1;
}
return l;
}
signed main(){
int T; cin>>T;
for(int i=1;i<=T;i++) printf("Case #%lld: %lld\n",i,solve());
return 0;
}