题意:
给一棵 n
个点的无根树,每个点的 权值为 ai
,每条边 长度为 1
,选一个点 v
作为 根节点 使得
最大,其中 dist(i, v)
表示 i
到 v
的简单路径的长度(n <= 2e5
)。
思路:
换根 dp,二次扫描法
用 dp[i]
表示:以 i
为根节点,下方式子的 答案
第一次 dfs
,假定 以 1
为根节点,预处理出此时 各个节点 i
的深度情况 depth[i]
之后,可以根据 公式 求出 dp[1]
。
第二次 dfs
,枚举 以各个节点 i
为根节点 时,对 先前的答案 dp[1]
的 贡献情况(即造成的影响、变化),算出 每一个 dp[i]
最后 循环枚举 dp
数组取最大值,即为 答案。
代码:
#include <bits/stdc++.h>
using namespace std;
//#define map unordered_map
#define int long long
const int N = 2e5 + 10, M = N << 1;
vector<int> g[N];
int a[N];
int n;
int depth[N];
int dp[N];
int sum, sum1[N];
int dfs1(int u, int f)
{
sum1[u] = a[u];
depth[u] = depth[f] + 1;
if (u == 1) depth[u] = 0;
for (auto v : g[u])
{
if (v == f) continue;
sum1[u] += dfs1(v, u);
}
return sum1[u];
}
void dfs2(int u, int f)
{
for (auto v : g[u])
{
if (v == f) continue;
dp[v] = dp[u] - sum1[v] + (sum - sum1[v]);
dfs2(v, u);
}
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; ++i)
{
scanf("%lld", &a[i]);
sum += a[i];
}
int t = n - 1;
while (t--)
{
int u, v; scanf("%lld%lld", &u, &v);
g[u].emplace_back(v);
g[v].emplace_back(u);
}
dfs1(1, 0);
for (int i = 1; i <= n; ++i) {
dp[1] += a[i] * depth[i];
}
dfs2(1, 0);
cout << *max_element(dp + 1, dp + n + 1) << '\n';
return 0;
}