洛谷 P3478 [POI2008] STA-Station

题目描述

给定一个 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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值