The rerooting technique
rerooting 是求解一类与树相关的问题的技巧。这类问题的特征:
- 定义了一个以有根树为自变量的函数
。 - 给你一个树
。 - 需要求出函数
在以 的每个点为根所得到的有根树上的值。
rerooting 在李煜东的《算法竞赛进阶指南》上名为“二次扫描与换根法”。
记号
设
Rerooting 的代码实现
过程
第一次扫描:通过典型的树上DP算出
第二次扫描:深度优先遍历
代码框架
#include <vector>
const int max_n = 100000; // number of vertices of the tree
std::vector<std::vector<int>> g(max_n);// store the tree, vertices are numbered from 0
void build_the_subtree_rooted_at(int u, int p) {
for (int v : g[u]) {
if (v != p) {
build_the_subtree_rooted_at(v, u);
// ...
}
}
}
// reroot the whole tree at u whose parent was p.
void reroot(int u, int p) {
// p lost a son u
// u got a son p
// modify u but DO NOT modify p
// It suffices to calculate what p becomes if it lost the son u,
// you don't have to actually modify p.
}
void dfs(int u, int p) {
// compute f(u)
// other things...
for (int v : g[u]) {
if (v != p) {
reroot(v, u);
dfs(v, u);
}
}
}
int main() {
build_the_subtree_rooted_at(0, -1);
dfs(0, -1);
}
解释
计算
- 依赖于
和 的全局信息,这类信息一般可以在第一次扫描的过程中计算出来。 - 依赖于
的信息,把它记作 , 是一个关于有根树的函数。
reroot(
,在 算出来之后即被 取代。- 关于
的除了 之外的其他信息。 - 关于
的全局信息。 。- 关于
的信息。
第 1, 2, 3 类信息都是在第一次扫描过程中算出来的,在第二次扫描过程中保持不变。在调用 reroot(
Examples
CF1467E. Distinctive Roots in a Tree
提交:116070391
CF1092F. Tree with Maximum Cost
提交:116069700
提交:31002137
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· “你见过凌晨四点的洛杉矶吗?”--《我们为什么要睡觉》
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· C# 从零开始使用Layui.Wpf库开发WPF客户端
· C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)
· 接口重试的7种常用方案!