牛客周赛 Round 84——小红的陡峭值(四)
牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
小红的陡峭值(四)
题目:
思路:
题目告诉我们关于树的陡峭值的定义,那一开始看起来无从下手,但是当我们选取某一个节点为根节点时,这个题目便看起来好做多了
我们这里选取节点1为根节点,那么接下来的每个节点的父节点都是唯一且确定的,我们这里可以定义一个arr[i],表示以i为根节点的陡峭值之和,其实这个有点像前缀和,但是是在树上的
那我们可以用一个dfs轻而易举的得到每个节点的陡峭值,那么接下来我们该如何分成两颗树呢?
显然,直接枚举删除那条边即可,那么我们再来考虑两树的差值如何计算呢,我们可以画出下图
假设我们要删除uv这条边,那么下树的值很好看出,不就是arr[v]吗,那上树呢?
显而易见,就是arr[1] - arr[v],同时注意一点,由于删除了uv,所以这条边的奉献也要删除,最终我们可以获得以下式子
上树:arr[v]
下树:arr[1] - arr[v] - abs(u-v)
二者一减就是答案了,同时要注意维护所有情况的最小值
代码:
#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#define ll long long
using namespace std;
int n;
vector<vector<int>> g(100005);
ll arr[100005];
ll ans = 9e18;
void dfs2(int father,int self)
{
for (auto e : g[self])
{
if (e != father)
{
dfs2(self, e);
ll up = arr[1] - abs(e - self) - arr[e];
ll down = arr[e];
ans = min(ans, abs(up - down));
}
}
}
void dfs(int father, int self)
{
for (auto e : g[self])
{
if (e != father)
{
dfs(self, e);
arr[self] += arr[e] + abs(self - e);
}
}
}
void solve()
{
cin >> n;
for (int i = 0; i < n-1; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(0,1);
dfs2(0, 1);
cout << ans;
}
int main()
{
cin.tie(0)->sync_with_stdio(false);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}