题目大意:给定一棵树,每个点有自己的权值 w i w_i wi,你需要在其中若干个节点上造塔,塔的高度 h i h_i hi就是造塔的花费,对于每个节点 i i i,必须保证存在两座塔, u , v u,v u,v,使得 w i ≤ m i n ( h u , h v ) w_i\le min(h_u,h_v) wi≤min(hu,hv),且 i i i位于 u , v u,v u,v的简单路径上.求造塔的最小费用和.
解题思路
这个题有一个贪心的思想,首先需要考虑权值最大点的情况,为了满足最大点一定符合,那么最小的花费 c o s t m i n ≥ 2 ∗ w m a x cost_{min}\ge 2*w_{max} costmin≥2∗wmax,一定是有两个权值为 w m a x w_{max} wmax的塔,那我们考虑怎么放这两座塔可以使得它对整棵树的贡献最大.
还需要考虑的一个点是,对于每个叶子节点,都需要造一座塔,因为只有这样,叶子节点才能保证被遍历.在这个基础之上,我们考虑把两座权值最大的塔都放在叶子节点上,然后把权值最大的点放在根,这样可以尽可能的让权值最大的塔对其他点的贡献最大,我们考虑写成树形DP的话,就是不断地去更新子树的最大值,如果当前向上的过程中出现了比子树更大的点,那我们就将最下面的叶子节点中权值最大的替换成当前这个点的权值,然后不断向上传递.
再考虑这两座权值最大的塔怎么放.
如果根节点有两个即以上的子树,那我们把所有子树的最大值都拿出来,然后去所有值中的最大和次大值,替换成 h m a x h_{max} hmax.
如果根节点只有一棵子树,那么一座塔放在子树上,另一座塔只能放在根本身的位置了.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define syncfalse ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int N = 2e5+5;
ll h[N];
vector<int>edge[N];
ll ans = 0;
ll maxh[N];
int root = 0;
ll dfs(int rt, int fa){
maxh[rt]=h[rt];
if (edge[rt].size()==1&&rt!=root){
ans+=h[rt];
// cout << rt << " " << ans << "\n";
return maxh[rt];
}
ll now = 0;
for (auto x : edge[rt]){
if (x==fa)continue;
now=max(now, dfs(x, rt));
}
if (h[rt]>now&&rt!=root){
ans+=(h[rt]-now);
}
// cout << rt << " " << ans << "\n";
maxh[rt]=max(maxh[rt], now);
return maxh[rt];
}
int main(){
syncfalse
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int n;
cin>>n;
ll maxd = 0;
for (int i = 1; i <= n; ++i)cin>>h[i],maxd=max(maxd,h[i]);
for (int u, v, i = 1; i < n; ++i){
cin>>u>>v;
edge[u].push_back(v);
edge[v].push_back(u);
}
for (int i = 1; i <= n; ++i){
if (h[i]==maxd){
root=i;break;
}
}
dfs(root, 0);
if (edge[root].size()==1){
ans+=maxd;
for (auto x : edge[root]){
ans+=(maxd-maxh[x]);
}
}
else{
int maxd1 = 0, maxd2 = 0;
for (auto x : edge[root]){
if (maxh[x]>maxd1)maxd2=maxd1,maxd1=maxh[x];
else if (maxh[x]>maxd2)maxd2=maxh[x];
}
ans+=2*maxd-maxd1-maxd2;
}
cout << ans << "\n";
return 0;
}