P1131 [ZJOI2007] 时态同步 题解

P1131 [ZJOI2007] 时态同步 题解

简化题意

有一棵根节点为 \(S\) 树,其有 \(N\) 个节点,\(N - 1\) 条边。其中,第 \(i\) 条边连接节点 \(a_i\),\(b_i\),长度为 \(t_i\)。
现在,我们希望所有节点到根节点所需的距离相同。我们可以花 \(1\) 的代价使任意一条边的长度 \(+1\)。
求我们所需的最小代价。

解题思路

首先能发现一点,如果一个父节点的每个子节点都需要增加一定的长度,那么一定是直接在它们的父节点上面增加最合算。
也就是说,我们只要保证每个节点的子树中,所有到此节点的距离相等。可以理解为对于当前节点的所有子节点,把它们都拉到同一长度,同时要贪心地从靠近根节点向其子节点尝试。
那么我们就用树形 DP,设 \(dis_i\) 为使第 \(i\) 个节点的子树所有子节点到父节点所需的距离相等,需要的代价为多少。

我们按照树形 DP 的扩展思路,先从根节点遍历到所有的叶子节点,更改当前节点的距离,然后再向上传递。
可以得出,每次调整的代价为对于所有 \(u\) 的子节点 \(v\),代价为 \(dis_u - dis_v\) 的和。当然,先有 \(dis_v = t, dis_u = \max\{dis_v\}\)

这样,我们就可以得出最终的答案了。就是所有调整代价之和。

注意:不开 long long 见祖宗。

代码部分

#include <bits/stdc++.h>
using namespace std;
int n, root;
long long ans = 0; 
vector<pair<int, int> > g[500010];
long long dis[500010]; 
void dfs(int u, int fa)
{
	for(int i = 0;i < g[u].size();i ++)
	{
		int v = g[u][i].first, t = g[u][i].second;
		if(v == fa)//是父节点 
			continue; 
		dfs(v, u);
		dis[v] += t;//遍历完成,改变距离 
		dis[u] = max(dis[u], dis[v]);//先求出必须更改到的距离 
	}
	
	for(int i = 0;i < g[u].size();i ++)
	{
		int v = g[u][i].first, t = g[u][i].second;
		if(v == fa)//是父节点 
			continue;
		ans += dis[u] - dis[v];//答案更新
	}
}
int main()
{
	scanf("%d %d", &n, &root);
	for(int i = 1, a, b, t;i < n;i ++)
	{
		scanf("%d %d %d", &a, &b, &t);
		g[a].push_back({b, t});
		g[b].push_back({a, t});
	}
	
	dfs(root, 0);//从根节点开始遍历 
	
	printf("%lld\n", ans); 
	return 0;
}

posted on 2026-10-07 20:18  Leo_29  阅读(4)  评论(0)    收藏  举报

导航