题解: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;
}
posted @ 2025-11-25 18:46  CharlieCai2024  阅读(13)  评论(0)    收藏  举报