[yLOI2019] 梅深不见冬
给定一个根节点为 \(1\) 的有根树,一共 \(n\) 个节点。从这棵 \(n\) 个节点的树的 \(1\) 号节点出发,沿着树上的边行走。规定:在节点 \(u\) 时,要么在 \(u\) 的孩子中选择一个没有到达过的节点 \(v\) 并行走到 \(v\),要么选择回到 \(u\) 的父亲节点。
现在给每个节点一个权值 \(w\),其中 \(i\) 号节点的权值为 \(w_i\)。他想给这棵树的某个节点放上从梅岭带出的梅花。我们规定能在节点 \(u\) 放上梅花当且仅当满足如下条件:
当前在节点 \(u\)。
对于 \(u\) 的所有孩子 \(v\),节点 \(v\) 被放上了 \(w_v\) 朵梅花。
同时,可以在任意时刻收回任意节点上的梅花,在收回梅花时不需要走到对应节点。
Q:对于每个节点,如果他想在 \(i\) 号节点上放 \(w_i\) 朵梅花,那么他最少要从梅岭带出多少朵梅花。
A:设要节点 \(i\) 放置梅花所需的数量为 \(f_u\)。我们依次按照 \(a_1,a_2,a_3,\dots,a_m\) 的顺序给儿子放置梅花,在放置好 \(a_i\) 处的梅花后将其子树(不包含 \(a_i\))中的梅花全部回收。这样做显然是最优的。
那么
设仅有两个儿子节点 \(x,y\)。设先 \(x\) 后 \(y\) 最优。
有
若左式取 \(f_x\),则 \(f_x\le f_x+w_y\le \max(f_y,w_y+f_x)\)。
若左式取 \(w_x+f_y\),则需满足 \(w_x+f_y\le w_y+f_x\)。
即 \(w_x-f_x\le w_y-f_y\)。
当最优解为 \(a_1,a_2,\dots,a_m\) 时:若最优解中满足 \(w_x-f_x\le w_y-f_y\) 的 \(x\) 在 \(y\) 的右边,交换 \(x\)、\(y\) 一定不劣。
因此我们可以不断的进行类似冒泡排序的方式交换 \(x,y\),使得最优解 \(a\) 满足 \(w_i-f_i\) 单调递增。
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+1;
int h[N],nt[N<<1],to[N<<1],cnt;
int f[N],w[N],n;
bool cmp(int x,int y){
return f[x]-w[x]>f[y]-w[y];
}
inline void lnk(int x,int y){
nt[++cnt]=h[x],h[x]=cnt,to[cnt]=y;
}
inline void dfs(int u){
f[u]=w[u];
for(int i=h[u],v;i;i=nt[i])dfs(v=to[i]),f[u]+=w[v];
vector<int> S; S.clear();
for(int i=h[u];i;i=nt[i])S.push_back(to[i]);
sort(S.begin(),S.end(),cmp); int cur=0;
for(vector<int>::iterator it=S.begin();it!=S.end();it++)
f[u]=max(f[u],cur+f[*it]),cur+=w[*it];
}
int main(){
cin>>n;
for(int i=2,x;i<=n;i++)cin>>x,lnk(x,i);
for(int i=1;i<=n;i++)cin>>w[i];
dfs(1);
for(int i=1;i<=n;i++)cout<<f[i]<<' ';
return 0;
}

浙公网安备 33010602011771号