Paths on the Tree

思路

我们从 \(|c_u-c_v|\le 1\) 切入。设节点 \(u\)\(k\) 个儿子。则根据小学就学过的鸽巢原理可以得到 \(u\) 的每个儿子至少会分到 \(\left\lfloor \dfrac{c_u}{k}\right\rfloor\)条路径,最多分到 \(\left\lceil\dfrac{c_u}{k}\right\rceil\)。而且分到 \(\left\lceil\dfrac{c_u}{k}\right\rceil\) 条路径的儿子个数 \(rest=c_u\bmod k\)。由于 \(u\) 的父亲也可能给 \(u\) 多分配一条路径,所以 \(u\) 的儿子分配到的路径条数有 4 种情况:$ \left\lfloor\dfrac{c_u}{k}\right\rfloor,\left\lceil\dfrac{c_u}{k}\right\rceil,\left\lfloor\dfrac{c_u+1}{k}\right\rfloor,\left\lceil\dfrac{c_u+1}{k}\right\rceil$。其中,最小的 \(\left\lfloor\dfrac{c_u}{k}\right\rfloor\) 和最大的 \(\left\lceil\dfrac{c_u+1}{k}\right\rceil\) 相差不会超过 1,所以一个节点分配到的节点数只有两种情况。

然后我们来决定这 \(rest\) 个多分配一条路径的机会分给那些儿子。显然哪些儿子多分配后增加的收益最大就分配给谁,用递归去求解。

Code

#include<bits/stdc++.h>
#define int long long
#define push_back emplace_back
using namespace std;
const int N = 2e5+25;
const int INF = 1e9+7;
int T;
int n,k,s[N];
vector<int>g[N];//存边
map<int,int>f[N];//记录每个节点带来的收益
int dfs(int u,int m){
	if(m==0)return 0;
	if(f[u].count(m))return f[u][m];
	if(g[u].size()==0)return f[u][m]=m*s[u];
	int t=m/g[u].size(),rest=m%g[u].size();
	int res=0;
	for(auto v:g[u])res+=dfs(v,t);
	if(rest){
		priority_queue<int>q;//将增加的收益用优先队列来维护
		for(auto v:g[u])q.emplace(dfs(v,t+1)-dfs(v,t));
		for(int i=0;i<rest;i++)res+=q.top(),q.pop();
	}
	res+=m*s[u];
	return f[u][m]=res;
}
void solve(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)
		g[i].clear(),f[i].clear();
	for(int i=2,u;i<=n;i++)
		cin>>u,g[u].push_back(i);
	for(int i=1;i<=n;i++)cin>>s[i];
	cout<<dfs(1,k)<<'\n';
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>T;
	while(T--)solve();
	return 0;
}
posted @ 2022-12-09 13:49  ASnown  阅读(31)  评论(0编辑  收藏  举报