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;
}
浙公网安备 33010602011771号