题目描述
给定一个 n 个点的树,请求出一个结点,使得以这个结点为根时,所有结点的深度之和最大。
一个结点的深度之定义为该节点到根的简单路径上边的数量。
输入格式
第一行有一个整数,表示树的结点个数 n。
接下来 (n−1) 行,每行两个整数 u,v,表示存在一条连接 u,v 的边。
输出格式
本题存在 Special Judge。
输出一行一个整数表示你选择的结点编号。如果有多个结点符合要求,输出任意一个即可。
输入输出样例
输入 #1复制
8 1 4 5 6 4 5 6 7 6 8 2 4 3 4
输出 #1复制
7
说明/提示
样例 1 解释
输出 77 和 88 都是正确答案。
数据规模与约定
对于全部的测试点,保证 1≤n≤10^6,1≤u,v≤n,给出的是一棵树。
这是一道比较好想的树形DP。我们定义dp[i]表示以i为根的树的深度和。
首先以1为根DFS一遍求出以每个点为根的子树的结S[i]表示以i为根的子树中结点个数。再dfs遍历整棵树,这里用u表示当前结点,用v表示每个儿子结点,则有
dp[v] = dp[u] + n - 2 * S[v]。
实际也是很容易想的,本来是以u为根的树,变成以儿子v为根的树,那么v的所有结点的深度都会减1,深度和就会减少size[v],同样地,所有不在v的子树上的结点的深度都会+1,深度和就会加上n - S[v],可以自行画图证明。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
vector<vector<int>> G;//邻接矩阵
vector<int> S;//每个点的子结点个数
int dp[100005], depth[100005];
int dfs1(int u, int fa)//求出每个点的子结点个数与深度
{
depth[u] = depth[fa] + 1;
for (int i = 0;i < G[u].size();++i)
{
int v = G[u][i];
if (v == fa)
continue;
else S[u] += dfs1(v, u);
}
S[u]++;//每个点作为根都至少有一个节点,也就是他自己
return S[u];
}
void dfs2(int u, int fa)
{
for (int i = 0;i < G[u].size();++i)
{
int v = G[u][i];
if (v == fa)
continue;
else
{
dp[v] = dp[u] + n - 2 * S[v];
dfs2(v, u);
}
}
}
signed main()
{
cin >> n;
G.resize(n);
S.resize(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);
}
dfs1(1, 0);
for (int i = 1;i <= n;i++)
dp[1] += depth[i];
dfs2(1, 0);
int m = 0, ans;
for (int i = 1;i <= n;i++)
{
if (dp[i] > m)
{
m = dp[i];
ans = i;
}
}
cout << ans;
return 0;
}