From a057b134e02bdc0b66e30b3669a9554aebe4fc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=B5=B7?= <10402852@qq.com> Date: Tue, 9 Jan 2024 10:18:43 +0800 Subject: [PATCH] 'commit' --- TangDou/Topic/HuanGenDp/P3478.cpp | 38 +++++++++------ TangDou/Topic/【换根】dfs专题.md | 65 ++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/TangDou/Topic/HuanGenDp/P3478.cpp b/TangDou/Topic/HuanGenDp/P3478.cpp index 37addb1..236de73 100644 --- a/TangDou/Topic/HuanGenDp/P3478.cpp +++ b/TangDou/Topic/HuanGenDp/P3478.cpp @@ -4,51 +4,61 @@ const int N = 1000010, M = N << 1; #define int long long #define endl "\n" -int n, id; // 链式前向星 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c = 0) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } -int ans; -int f[N], dep[N], size[N]; +int n; // n个节点 +int depth[N]; // depth[i]:在以1号节点为根的树中,i号节点的深度是多少 +int sz[N]; // sz[i]:以i号节点为根的子树中有多少个节点 +int f[N]; // DP结果数组,f[i]记录整个树以i为根时,可以获取到的深度和是多少 + +// 第一次dfs void dfs1(int u, int fa) { - size[u] = 1; - dep[u] = dep[fa] + 1; + sz[u] = 1; // 以u为根的子树,最起码有u一个节点 + depth[u] = depth[fa] + 1; // u节点的深度是它父节点深度+1 for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; if (v == fa) continue; - dfs1(v, u); - size[u] += size[v]; + dfs1(v, u); // 深搜v节点,填充 sz[v],depth[v] + sz[u] += sz[v]; // 在完成了sz[v]和depth[v]的填充工作后,利用儿子更新父亲的sz[u]+=sz[v]; } } + +// 第二次dfs void dfs2(int u, int fa) { for (int i = h[u]; ~i; i = ne[i]) { int v = e[i]; if (v == fa) continue; - f[v] = f[u] + n - 2 * size[v]; + f[v] = f[u] + n - 2 * sz[v]; dfs2(v, u); } } + signed main() { - memset(h, -1, sizeof h); + memset(h, -1, sizeof h); // 初始化链式前向星 + cin >> n; for (int i = 1; i < n; i++) { // n-1条边 int a, b; cin >> a >> b; add(a, b), add(b, a); // 换根DP,无向图 } - // 1、第一次dfs,找出:xxx + + // 1、第一次dfs,以1号节点为根,它的父节点不存在,传入0 dfs1(1, 0); // 2、换根 - for (int i = 1; i <= n; i++) f[1] += dep[i]; - dfs2(1, 0); + for (int i = 1; i <= n; i++) f[1] += depth[i]; // DP初始化,以1号节点为根时,所有节点的深度和 + dfs2(1, 0); // 从1号节点开始,深度进行换根 // 3、找答案 - for (int i = 1; i <= n; i++) - if (ans < f[i]) ans = f[i], id = i; + int ans = 0, id = 0; + for (int i = 1; i <= n; i++) // 遍历每个节点 + if (ans < f[i]) ans = f[i], id = i; // ans记录最大的深度值,id记录以哪个节点为根时取得最大值 + // 输出以哪个节点为根时,深度和最大 cout << id << endl; } \ No newline at end of file diff --git a/TangDou/Topic/【换根】dfs专题.md b/TangDou/Topic/【换根】dfs专题.md index 5b4b9ac..a1eb9d7 100644 --- a/TangDou/Topic/【换根】dfs专题.md +++ b/TangDou/Topic/【换根】dfs专题.md @@ -39,7 +39,72 @@ $$f[v]=f[u]-siz[v]+(siz[1]-siz[v]),fa[v]=u$$ 简化一下就是 $$f[v]=f[u]+siz[1]-2\times siz[v]=f[u]+n-2\times siz[v]$$ +```cpp {.line-numbers} +#include +using namespace std; +const int N = 1000010, M = N << 1; +#define int long long +#define endl "\n" + +// 链式前向星 +int e[M], h[N], idx, w[M], ne[M]; +void add(int a, int b, int c = 0) { + e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; +} + +int n; // n个节点 +int depth[N]; // depth[i]:在以1号节点为根的树中,i号节点的深度是多少 +int sz[N]; // sz[i]:以i号节点为根的子树中有多少个节点 +int f[N]; // DP结果数组,f[i]记录整个树以i为根时,可以获取到的深度和是多少 + +// 第一次dfs +void dfs1(int u, int fa) { + sz[u] = 1; // 以u为根的子树,最起码有u一个节点 + depth[u] = depth[fa] + 1; // u节点的深度是它父节点深度+1 + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (v == fa) continue; + dfs1(v, u); // 深搜v节点,填充 sz[v],depth[v] + sz[u] += sz[v]; // 在完成了sz[v]和depth[v]的填充工作后,利用儿子更新父亲的sz[u]+=sz[v]; + } +} + +// 第二次dfs +void dfs2(int u, int fa) { + for (int i = h[u]; ~i; i = ne[i]) { + int v = e[i]; + if (v == fa) continue; + f[v] = f[u] + n - 2 * sz[v]; + dfs2(v, u); + } +} + +signed main() { + memset(h, -1, sizeof h); // 初始化链式前向星 + + cin >> n; + for (int i = 1; i < n; i++) { // n-1条边 + int a, b; + cin >> a >> b; + add(a, b), add(b, a); // 换根DP,无向图 + } + + // 1、第一次dfs,以1号节点为根,它的父节点不存在,传入0 + dfs1(1, 0); + + // 2、换根 + for (int i = 1; i <= n; i++) f[1] += depth[i]; // DP初始化,以1号节点为根时,所有节点的深度和 + dfs2(1, 0); // 从1号节点开始,深度进行换根 + + // 3、找答案 + int ans = 0, id = 0; + for (int i = 1; i <= n; i++) // 遍历每个节点 + if (ans < f[i]) ans = f[i], id = i; // ans记录最大的深度值,id记录以哪个节点为根时取得最大值 + // 输出以哪个节点为根时,深度和最大 + cout << id << endl; +} +``` **总结与进阶**