树学 树形DP-换根

 链接:https://ac.nowcoder.com/acm/problem/201400
来源:牛客网

题目描述

牛妹有一张连通图,由n个点和n-1条边构成,也就是说这是一棵树,牛妹可以任意选择一个点为根,根的深度deprootdep_{root}deproot为0,对于任意一个非根的点,我们将他到根节点路径上的第一个点称作他的父节点,例如1为根,1-4的;路径为1-3-5-4时,4的父节点是5,并且满足对任意非根节点,depi=depfai+1dep_i=dep_{fa_i}+1depi=depfai+1,整棵树的价值W=∑i=1ndepiW=\sum\limits_{i=1}^n dep_iW=i=1ndepi,即所有点的深度和

牛妹希望这棵树的W最小,请你告诉她,选择哪个点可以使W最小

输入描述:

第一行,一个数,n
接下来n-1行,每行两个数x,y,代表x-y是树上的一条边

输出描述:

一行,一个数,最小的W
示例1

输入

复制
4
1 2
1 3
1 4

输出

复制
3

备注:

对于30%30\%30%的数据,1≤n≤10001\leq n \leq 10001n1000
对于100%100\%100%的数据,1≤n≤1061\leq n \leq 10^61n106

分析

换根题,先预处理出以 1 为根节点所有点的深度和以当前节点为根的子节点个数。

注意到将叶子节点放到当前根节点之上,就是换根后的图形。

所有当前叶子节点的深度 -1 ,所有除了当前 子树 的节点深度 +1

设f[u] 表示以 u 节点为根的所有节点深度和

状态转移:f[v] = f[u] - siz[v] + (n - siz[v]);//siz表示以v为根的子树的节点数量

 

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long ll;
const int N = 1e6 + 10, M = N << 1;
int n, f[N];
int h[N], e[M], ne[M], idx;
void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;  
}

int dep[N], siz[N];
void dfs1(int u, int fa) {
    siz[u] = 1;
    for(int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if(v == fa) continue;
        dep[v] = dep[u] + 1;
        dfs1(v, u);
        siz[u] += siz[v];
    }
}

void dfs2(int u, int fa) {
    for(int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if(v == fa) continue;
        f[v] = f[u] - siz[v] + (n - siz[v]);
        dfs2(v, u);
    }
}

int main()
{
    memset(h, -1, sizeof h);
    scanf("%d", &n);
    for(int i = 1; i < n; i++) {
        int a, b; scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    dfs1(1, 0);
    for(int i = 1; i <= n; i++) f[1] += dep[i];
    dfs2(1, 0);
    int res = f[1];
    for(int i = 2; i <= n; i++) res = min(res, f[i]);
    printf("%d\n", res);
    return 0;
}

 

posted @ 2022-08-04 14:36  er007  阅读(41)  评论(0编辑  收藏  举报