P8966 题解(2023 激励计划评分 8)
前言
一年前赛时过的题,来补个题解。
解法
显然凡人只能投一种电荷(不妨设为正电荷),否则必然不优,原因显然。
因为神明要让游戏轮数变多,所以如果它可以决策,都得往与目标点所在方向的相反的方向扔。而又因为要左右子树电荷数平衡,所以先得在另一棵子树填上与目标所在子树的答案相等数量的电荷,注意另一棵子树的子树和不一定大于目标所在子树的答案,此时把另一棵子树填满即可。
枚举每一种目标结点,不断往上找父亲并更新答案。具体地,令另外一棵子树大小为
放代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
main(){
ios::sync_with_stdio(false);
int n; cin>>n;
vector<int> p(n,-1),c(n),s(n);
vector<array<int,2> > k(n,{-1,-1});
for(int i=1;i<n;i++){
int x; cin>>x; p[i]=--x;
(~k[x][0]?k[x][1]:k[x][0])=i;
} // 建树
for(auto &i:c)cin>>i;
function<void(int)> dfs=[&](int u){
if(~k[u][0])dfs(k[u][0]),s[u]+=s[k[u][0]];
if(~k[u][1])dfs(k[u][1]),s[u]+=s[k[u][1]];
}; // 预处理子树和
s=c,dfs(0);
for(int i=0;i<n;i++){
int x=i,c=s[i];
while(~p[x]){
int b=(x==k[p[x]][0]?k[p[x]][1]:k[p[x]][0]);
c+=min(c,~b?s[b]:0),x=p[x];
} // 暴力找父亲,更新答案
cout<<c<<' ';
}
cout<<endl;
return 0;
}