换根dp

news/2025/2/26 22:34:58/文章来源:https://www.cnblogs.com/Natural-TLP/p/18739579

概念

换根 \(dp\) ,又被称为二次扫描,是属于树形 \(dp\) 的一类但比一般树形dp更难。

特点

  • 通常是没有指定根结点,且根结点的变化会对一些值产生影响。

  • 通常需要两次 \(dfs\) ,第一次 \(dfs\) 预处理信息,第二次 \(dfs\) 开始换根动态规划。

  • 求解的答案通常需要结合所有相连的结点,且一般都是多次询问某个点的答案。

优点

暴力求解的话,枚举每个结点作为根的情况再 \(dfs\) 扫描,这样就需要 \(O(n^2)\) 的时间复杂度,通常是不能够接受。而换根 \(dp\),先进行一次扫描预处理信息后,再一次扫描进行动态规划解出所有节点的答案,时间复杂度优化为 \(O(n + n)\) ,这样就可以成功获取答案了。

解法一般形式

换根 \(dp\) 第一次扫描通常需要结合树形 \(dp\) 的思想,先任选一个结点 \(root\) 作为根结点,然后从根结点开始递归处理信息,但这时只有以 \(root\) 作为根结点的信息,所以需要在第二次扫描时,考虑换另一个结点为根时的答案,这时要通过第一次预处理出来的信息进行状态转移。

即:

  • 以某个点(通常是 \(1\))作为根节点进行第一次扫描,预处理信息。

  • 依旧从这个点开始第二次扫描,但这次进行换根的动态规划,通常是结合父节点的信息合并统计答案。

题目讲解

数的中心

题目链接:AcWing 树的中心、洛谷 树的中心

题目大意:

给定一棵树,求这个树的中心。
树的中心:树上某个结点到最远的结点距离最近,那么这个结点就是树的中心。

思路:

首先建立以 \(1\) 为根的树,然后思考每个结点的最长距离会出现的路径:当前结点从某个子节点出发的最长路径,或从父节点出发的不再经过自己的最长路径。

所以,我们可以先第一次 \(dfs\) 扫描出每个结点从子结点出发的最长距离和次长距离,第二次扫描时结合父节点的数据更新当前结点为根时的最长距离。

处理次长距离的原因是:在进行换根的动态转移时,要结合父节点的最长路径,但如果父节点的最长路径恰好经过了当前结点,那么就要用父节点的次长路径来进行状态转移了。

AcWing代码
#include <iostream>
#include <cstring>using namespace std;const int INF = 0x3f3f3f3f;
const int N = 2e4 + 10;int n;
int h[N], e[N], w[N], ne[N], idx;
int d1[N], d2[N], up[N];void add(int a, int b, int c)
{e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}void dfs_d(int u, int fa)
{d1[u] = d2[u] = -INF;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == fa) continue;dfs_d(j, u);if (d1[j] + w[i] > d1[u]) d2[u] = d1[u], d1[u] = d1[j] + w[i];else if (d1[j] + w[i] > d2[u]) d2[u] = d1[j] + w[i];}if (d1[u] == -INF) d1[u] = d2[u] = 0;
}void dfs_u(int u, int fa)
{for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == fa) continue;up[j] = up[u] + w[i];if (d1[j] + w[i] != d1[u]) up[j] = max(up[j], d1[u] + w[i]);else up[j] = max(up[j], d2[u] + w[i]);dfs_u(j, u);}
}int main()
{memset(h, -1, sizeof h);cin >> n;for (int i = 1; i < n; i ++){int a, b, c;cin >> a >> b >> c;add(a, b, c), add(b, a, c);}dfs_d(1, -1);dfs_u(1, -1);int res = INF;for (int i = 1; i <= n; i ++) res = min(res, max(d1[i], up[i]));cout << res;return 0;
}
洛谷代码
#include <iostream>
#include <cstring>using namespace std;const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;int n;
int h[N], e[N], ne[N], idx;
int d1[N], d2[N], up[N];void add(int a, int b)
{e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}void dfs_d(int u, int fa)
{d1[u] = d2[u] = -INF;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == fa) continue;dfs_d(j, u);if (d1[j] + 1 > d1[u]) d2[u] = d1[u], d1[u] = d1[j] + 1;else if (d1[j] + 1 > d2[u]) d2[u] = d1[j] + 1;}if (d1[u] == -INF) d1[u] = d2[u] = 0;
}void dfs_u(int u, int fa)
{for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == fa) continue;up[j] = up[u] + 1;if (d1[j] + 1 != d1[u]) up[j] = max(up[j], d1[u] + 1);else up[j] = max(up[j], d2[u] + 1);dfs_u(j, u);}
}int main()
{memset(h, -1, sizeof h);cin >> n;for (int i = 1; i < n; i ++){int a, b;cin >> a >> b;add(a, b), add(b, a);}dfs_d(1, -1);dfs_u(1, -1);int res = INF;int ans1, ans2 = -1;for (int i = 1; i <= n; i ++) {int d = max(d1[i], up[i]);if (d < res) {res = d;ans1 = i;}else if (d == res) ans2 = i;}cout << ans1;if (~ans2 && max(d1[ans2], up[ans2]) == res) cout << ' ' << ans2;return 0;
}

[POI 2008] STA-Station

题目链接:洛谷 P3478

题目大意:

给定一个 \(n\) 个点的树,请求出一个结点,使得以这个结点为根时,所有结点的深度之和最大。

思路:

首先,我们依旧建立以 \(1\) 为根的树。定义 \(dp[i]\) 为:以 \(i\) 为根时深度之和。

接着,思考换根的状态转移,以样例距离

假设我们已经求出了以 \(1\) 为根的深度之和,那么当以 \(4\) 为根时,可以发现以 \(4\) 为根的子树的所有结点深度都减少了1,而不属于 \(4\) 为根的子树的结点的深度都增加了1,再举例以 \(5\) 为根时对比以 \(4\) 为根时也符合上面的推测,得出转移的公式:\(dp[v] = dp[u] - size[v] + n - size[v]\)

从上面的转移公式,可以知道我们第一次扫描需要预处理子树的结点个数和 \(dp[1]\) 的值。

点击查看代码
#include <iostream>
#include <vector>using namespace std;typedef long long ll;const int N = 1e6 + 10;int n;
vector<int> g[N];
ll c[N], dp[N], sum, ans;void dfs1(int u, int fa, int h)
{c[u] = 1;dp[1] += h;for (auto v : g[u]) {if (v == fa) continue;dfs1(v, u, h + 1);c[u] += c[v];}
}void dfs2(int u, int fa)
{for (auto v : g[u]) {if (v == fa) continue;dp[v] = dp[u] + n - 2 * c[v];dfs2(v, u);}if (dp[u] > sum) {sum = dp[u];ans = u;}
}int main()
{cin >> n;for (int i = 1; i < n; i ++) {int u, v;cin >> u >> v;g[u].emplace_back(v);g[v].emplace_back(u);}dfs1(1, -1, 0);dfs2(1, -1);cout << ans;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/890398.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

千锋教育MyBatisPlus全套课程,简单快速一套精通MyBatisPlus框架(代码生成器_引入_使用)

https://www.bilibili.com/video/BV1aa4y1A7iN?spm_id_from=333.788.videopod.episodes&vd_source=0d7b1712ce42c1a2fa54bb4e1d601d78代码生成器_引入_使用 https://github.com/godmaybelieve

【PLSQL】使用PLSQL查看创表SQL踩坑

背景 通过PL/SQL Developer的View SQL查看表的创表语句,结果发现创表语句少了字段 可能原因PL/SQL Developer 的对象浏览器会缓存元数据信息以提高性能。如果修改表结构后未手动刷新,工具可能继续展示缓存中的旧元数据。 Oracle 的数据字典视图(如 USER_TAB_COLUMNS)本身是…

从 0 到 Offer:Dynamics 365 CRM 学员的普华永道逆袭之路

在竞争激烈的就业市场中,如何才能脱颖而出,收获理想的工作?李先顺(化名)的经历或许能给我们带来深刻的启示。这位出身普通高校信息管理专业的应届毕业生,通过在爱码士IT培训www.aimashi365.com机构的报名和培训,凭借着对 Dynamics 365 CRM 技术的深入学习和实践,成功入…

本地?线上?分布式系统前后端架构、部署、联调指南,突破技术

“ 引言:对于常见的BS架构系统,程序员如何进行本地或者线上环 境联调,这有助于提高个人工作效率,站在更高的角度审视系统, 从此以后再无惧Bug,让你早干完活,早摸鱼🐟,早下班。 对于Java初学者,或者是三年工作经验的“新手” ,希望此文对你有所裨益! -- 诗经有云,…

Spring AI 学习之路 快速上手

随着人工智能(AI)技术的迅速发展,越来越多的开发者开始关注如何在自己的应用中集成 AI 功能。Spring 框架作为一种流行的 Java 开发框架,提供了强大的支持来构建现代应用程序。本文将为你介绍如何快速上手 Spring AI,帮助你在项目中轻松集成 AI 功能。什么是 Spring AI? …

【PWN】初识Orw

例题:NPCCTF - Ooooorw发现开启了沙箱,禁用了execve函数,所以只能利用open,read,write函数来进行输出flag from pwn import *file = ./pwnlibc = ELF(./libc.so.6)i = 0if i == 1: io = process(file)else: io = remote(175.27.249.18,32438)elf = ELF(file)context…

洛谷2025省选模拟赛D1T1“Ball” 题解

洛谷2025省选模拟赛D1T1“Ball” 题解首先可以写出一个暴力的 dp : 令 \(f_{i,j}\) 表示有 \(i\) 个选了一次的球和 \(j\) 个选了大于一次的球的期望答案,有等式: \[f_{i,j}=\frac jnf_{i,j}+\frac{n-i-j}nf_{i+1,j}+\frac in f_{i-1,j+1} \]进一步得 \(f_{i,j}=\frac{n-i-j…

[计算机网络/网络抓包/以太网] `.pcap` 数据报文存储格式

序:一份以太网报文概述:.pcap 数据报文存储格式 简介.pcap文件:一种常用的数据报文存储格式,主要用于网络数据包的捕获和存储。pcap文件格式由PCAP库提供,支持将网络上的所有数据包保存到文件中,并可以从文件中读取数据包。 pcap文件通常由文件头和数据包头组成,每个数据…

C#/.NET/.NET Core技术前沿周刊 | 第 27 期(2025年2.17-2.23)

前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿,助力技术成长与视野拓宽。欢迎投稿、推荐或自荐优质文章、项目、学习资源等…

Jenkins详解教程

1.介绍2.Jenkins CI/CD 流程3.部署Jenkins查看初始密码4 使用Jenkins

Redis概述安装与基本数据类型

Redis概述安装与基本数据类型 1. 什么是NoSQL NoSQL( Not Only SQL ),意即不仅仅是SQL, 泛指非关系型的数据库。Nosql这个技术门类,早期就有人提出,发展至2009年趋势越发高涨。 2. NoSQL的广泛应用 随着大数据的兴起,数据量的暴增,数据类型的丰富,传统的关系数据库在应付动…