首先,贪心地想,为了最大化每条链对答案的贡献,肯定是要走到叶子节点的。
考虑如何对于节点 \(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;
}