树学 树形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=1∑ndepi,即所有点的深度和
牛妹希望这棵树的W最小,请你告诉她,选择哪个点可以使W最小
牛妹希望这棵树的W最小,请你告诉她,选择哪个点可以使W最小
输入描述:
第一行,一个数,n
接下来n-1行,每行两个数x,y,代表x-y是树上的一条边
输出描述:
一行,一个数,最小的W
备注:
对于30%30\%30%的数据,1≤n≤10001\leq n \leq 10001≤n≤1000
对于100%100\%100%的数据,1≤n≤1061\leq n \leq 10^61≤n≤106
分析
换根题,先预处理出以 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; }