链接
https://ac.nowcoder.com/acm/contest/4370/H
题意
将一个 $n$ 个节点的点权树切为 $k$ 个树,使每棵树的点权和的最大值最小。
思路
二分答案。
记 $f_i$ 为节点 $i$ 与子树的权值和。
自底向上考虑,如果 $f_i$ 大于答案,则将子节点按照 $f$ 从大到小切,直到 $f_i$ 小于等于答案。
如果切了小于 $k$ 次,则该答案成立。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <bits/stdc++.h> #define SZ(x) (int)(x).size() #define ALL(x) (x).begin(),(x).end() #define PB push_back #define EB emplace_back #define MP make_pair #define FI first #define SE second using namespace std; typedef double DB; typedef long double LD; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> PII; typedef vector<int> VI; typedef vector<PII> VPII;
const int N=2e5+5; int n,k,tot; LL w[N],f[N]; VI g[N]; bool cmp(int a,int b) { return f[a]>f[b]; } bool dfs(int u,int fa,LL mx) { f[u]=w[u]; for(auto v:g[u]) { if(v==fa) continue; if(!dfs(v,u,mx)) return false; f[u]+=f[v]; } if(f[u]<=mx) return true; sort(ALL(g[u]),cmp); for(auto v:g[u]) { if(v==fa) continue; if(++tot>k) return false; f[u]-=f[v]; if(f[u]<=mx) return true; } return false; } bool check(LL mid) { tot=0; return dfs(1,0,mid); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin>>T; for(int cs=1;cs<=T;cs++) { cin>>n>>k; --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].PB(v); g[v].PB(u); } for(int i=1;i<=n;i++) cin>>w[i]; LL l=0,r=1e14; while(l<r) { LL mid=l+r>>1; if(check(mid)) r=mid; else l=mid+1; } cout<<"Case #"<<cs<<": "<<l<<'\n'; } return 0; }
|