题解:CF1746D Paths on the Tree
首先,贪心地想,为了最大化每条链对答案的贡献,肯定是要走到叶子节点的。
考虑如何对于节点 \(x\) 和其儿子 \(y_1,y_2\) 如何处理 \(|c_{y_1} - c_{y_2}| \le 1\) 的限制,可以视为要求把节点 \(x\) 所有向下走的链的数量尽量平均地分给它的儿子,假设链数为 \(sum\),\(x\) 的儿子数量为 \(son_x\),那么每个儿子至少能分到 \(\lfloor\frac{sum}{son_x}\rfloor\) 条链。
还剩下 \(sum \bmod son_x\)(记为 \(k\))条,为了最大化它们对答案的贡献,我们肯定选择让他们走到能产生贡献前 \(k\) 大的儿子。考虑 dfs 时对于点 \(x\) 如何计算它对父亲的这个贡献,不能走自己已经特殊关照过的 \(k\) 个儿子(否则就不满足前面的条件了),于是找到产生贡献第 \(k+1\) 大的儿子的贡献加上 \(s_x\) 上传给父亲供其计算,用一个优先队列维护一下即可。
#include<bits/stdc++.h>
#define int long long
#define MAXN 200005
using namespace std;
const int inf=1e18;
int T,n,m,cnt,head[MAXN],son[MAXN],w[MAXN],ans;
struct Edge{
int value,next;
}edge[MAXN*2];
void addedge(int u,int v){
edge[++cnt].value=v;
edge[cnt].next=head[u];
head[u]=cnt;
}
void dfs1(int x,int fa){
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].value;
if(y!=fa){
son[x]++;
dfs1(y,x);
}
}
}
int dfs2(int x,int fa,int k){
ans+=w[x]*k;
if(!son[x])return w[x];
int a=k/son[x],b=k%son[x];
priority_queue<int,vector<int>,less<int> >q;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].value;
if(y!=fa){
q.push(dfs2(y,x,a));
}
}
while(b--){
int x=q.top();q.pop();
ans+=x;
}
return q.top()+w[x];
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%lld",&T);
while(T--){
for(int i=1;i<=n;i++)head[i]=son[i]=0;
ans=cnt=0;
scanf("%lld%lld",&n,&m);
for(int i=2;i<=n;i++){
int u=i,v;scanf("%lld",&v);
addedge(u,v),addedge(v,u);
}
for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
dfs1(1,0);
dfs2(1,0,m);
printf("%lld\n",ans);
}
return 0;
}
浙公网安备 33010602011771号