题解:洛谷 P9128([USACO23FEB] Fertilizing Pastures G)

Link

1. Description

\(N\) 个顶点的树,经过节点之间的每一条边都需要 \(1s\)。每个顶点一开始的权值均为 \(0\),第 \(i\) 个点的权值的增长速率为 \(a_i/s\)。FJ 从 \(1\) 号顶点出发遍历整棵树。当 FJ 走到某个节点时,若该节点的权值为 \(x\),则需要支出大小为 \(x\) 的费用。(当然,只需在第一次经过该节点时需要支出。)

给出一个参数 \(T\):

  • \(T=0\),FJ 必须回到 \(1\) 号节点

  • \(T=1\),FJ 可以在任意节点结束他的遍历

求遍历所有节点的最小时间和此时需要付出的最小的费用。

2. Solution

\(T=0\) 时这个问题十分简单,我们考虑需要在最小时间内遍历整棵树并返回根节点,那么一条边就只会被遍历两次,所需时间为 \(2(n-1)\)

最小费用则是一个树形 DP,不妨设 \(sum_u\) 表示以 \(u\) 为根的子树中所有节点的增长速率之和,\(siz_u\) 表示以 \(u\) 为根的子树中的节点数,\(f_u\) 表示从 \(u\) 出发,遍历完以 \(u\) 为根的子树并返回 \(u\) 的最小代价,注意,此时认为从 \(u\) 出发时的时刻为 \(0\)

那么当我们以一个确定顺序 \(v_1,v_2,\cdots,v_{|son_u|}\) 遍历 \(u\) 的子树时,花费就是 \(\sum_{i=1}^{|son_u|} f_{v_i}+(2\times \sum_{j=1}^{i-1} siz_{v_j}+1)\times sum_{v_i}\),后一个部分的系数可以在遍历的过程中计算,那么时间复杂度就是 \(O(|son_u|)\),但是我们并不知道最优的顺序是什么,所以还需要暴力枚举所有顺序,时间复杂度就是 \(O(|son_u|!\times |son_u|)\),这显然是不够优秀的时间复杂度,所以我们考虑优化,这里显然可以使用贪心来确定一个最优顺序,直接邻项交换法,令之前已经耽误了 \(c\) 的时间,需要交换 \(x,y\) 两项,那么这两项在交换前后的贡献和就是 \(f_x+(c+1)\times sum_x+f_y+(c+1+2\times siz_x)\times sum_y\)\(f_y+(c+1)\times sum_y+f_x+(c+1+2\times siz_y)\times sum_x\), 不需要交换当且仅当:

\[f_x+(c+1)\times sum_x+f_y+(c+1+2\times siz_x)\times sum_y<f_y+(c+1)\times sum_y+f_x+(c+1+2\times siz_y)\times sum_x \]

也就是:

\[siz_x\times sum_y<sum_y+sum_x \]

那么直接排序然后遍历即可,时间复杂度为 \(O(n\log n)\)

\(T=1\) 的时候可能有些复杂,考虑最小时间一定是在一个深度最深的节点结束遍历时得到的,所以所需时间为 \(2(n-1)-\max_{i=1}^{n} dep_i\),其中 \(dep_1=0\)

最小费用我们延续 \(T=0\) 时的想法,设 \(mxdep_u\) 表示以 \(u\) 为根的子树中,所有节点的最大深度,\(f_{u,0/1}\) 分别表示从 \(u\) 开始,以最短时间遍历完以 \(u\) 为根的子树后不返回/返回 \(u\) 的最小费用,其中 \(f_{1,u}\) 的转移和 \(T=1\)\(f_u\) 的转移一致,我们考虑 \(f_{0,u}\) 的转移。

显然,我们为了保证花费时间最小,一定会选择一棵最大深度最小的子树进入之后不返回,我们假定这棵子树的根节点为 \(v\),在排序后的 \(son_u\) 数组中的下标为 \(pos\),那么我们首先在遍历时跳过 \(v\) 这棵子树,减少的贡献分为两部分:

  1. \(v\) 本身的贡献,也就是 \(f_{1,v}+(2\times \sum_{i=1}^{pos-1} siz_{v_i}+1)\times sum_v\),这个贡献可以在转移 \(f_{1,u}\) 的过程中统计。
  2. \(v\) 花费的时间对后面子树的贡献,也就是 \(2\times siz_v\times (\sum_{i=pos+1}^{|son_u|} sum_{v_i})\),所以我们可以倒序遍历 \(son_u\),那么后缀和就可以直接统计。

然后再最后遍历这棵子树,增加的贡献就是 \(f_{0,v}+sum_v\times [2\times (siz_u-siz_v-1)+1]\),所以 \(f_{0,u}=\min_{v\in son_u}f_{1,u}-val_v-2\times siz_v\times sufsum+f_{0,v}+sum_v\times (2siz_u-2siz_v-1)[mxdep_v=mxdep_u]\),其中 \(val_v\) 表示 \(v\)\(f_{0,u}\) 的贡献,\(sufsum\) 表示后缀 \(sum\) 的和。

3. Code

/*by qwer6*/
/*略去缺省源与快读快写*/
const int N=2e5+5,inf=8e18;
int n,type;
int a[N];
struct Graph{
	struct Edge{
		int v,nxt;
	}e[N];
	int cnt_edge;
	int head[N];
	void AddEdge(int u,int v){
		e[++cnt_edge]={v,head[u]};
		head[u]=cnt_edge;
	}
}G;
namespace Subtask1{//必须回到 1 结束遍历  
	int f[N],siz[N],sum[N];
	vector<int>son[N];
	bool cmp(int x,int y){
		return siz[x]*sum[y]<siz[y]*sum[x];
	}
	void dfs(int u){
		siz[u]=1,sum[u]=a[u];
		for(int i=G.head[u],v=G.e[i].v;i;i=G.e[i].nxt,v=G.e[i].v){
			dfs(v);
			siz[u]+=siz[v];
			sum[u]+=sum[v];
			son[u].push_back(v);
		}
		int c=0;
		sort(son[u].begin(),son[u].end(),cmp);
		for(int v:son[u]){
			f[u]+=f[v]+(c+1)*sum[v];
			c+=2*siz[v];
		}
	}
	void solve(){
		dfs(1);
		write((n-1)*2),Spa,write(f[1]),Nxt;
	}
}
namespace Subtask2{//可以在任意节点结束遍历  
	int dep[N],siz[N],sum[N],mxdep[N],val[N],f[2][N];
	vector<int>son[N];	
	bool cmp(int x,int y){
		return siz[x]*sum[y]<siz[y]*sum[x];
	}
	void dfs(int u){
		siz[u]=1,sum[u]=a[u],mxdep[u]=dep[u];
		for(int i=G.head[u],v=G.e[i].v;i;i=G.e[i].nxt,v=G.e[i].v){
			dep[v]=dep[u]+1;
			dfs(v);
			tomax(mxdep[u],mxdep[v]);
			siz[u]+=siz[v];
			sum[u]+=sum[v];
			son[u].push_back(v);
		}
		int c=0;
		sort(son[u].begin(),son[u].end(),cmp);
		for(int v:son[u]){
			val[v]=f[1][v]+(c+1)*sum[v];
			f[1][u]+=val[v];
			c+=2*siz[v];
		}
		f[0][u]=inf;
		for(int i=son[u].size()-1,suf=0,v;i>=0;i--){
			v=son[u][i];
			if(mxdep[v]!=mxdep[u]){
				suf+=sum[v];
				continue;
			}
			c=2*(siz[u]-siz[v]-1);
			tomin(f[0][u],f[1][u]-val[v]-2*siz[v]*suf+f[0][v]+(c+1)*sum[v]);
			suf+=sum[v];
		}
		if(f[0][u]==inf)f[0][u]=0;
	}
	void solve(){
		dfs(1);
		write((n-1)*2-mxdep[1]),Spa,write(f[0][1]),Nxt;
	}
}
signed main(){
	read(n),read(type);
	for(int i=2,fa;i<=n;i++){
		read(fa),read(a[i]);
		G.AddEdge(fa,i);
	}
	if(type==0)Subtask1::solve();
	else Subtask2::solve();
}
posted @ 2025-05-11 15:57  陈牧九  阅读(29)  评论(0)    收藏  举报