目录
一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
二、解题报告
1、思路分析
2、复杂度
3、代码详解
一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
1975D - Paint the Tree
二、解题报告
1、思路分析
我们从树中一点出发,遍历整颗树最后回到这个点的最小步数:2n - 2,即边数*2
不要求回到该点:2n-2 - maxd,maxd 为 从该点出发到达叶子的最远步数
PA要遍历整棵树,PB也要遍历整棵树
最佳的情况就是 PA PB 重合或相邻,PA按照最优情况遍历整棵树,PB跟着PA跑
但是 PA 和 PB 不重合或者相邻又该如何?
一个比较稳健的方式是:换根dp
怎么考虑呢?二者一定是在某个点相遇,然后PB按照最优方式遍历这棵树
那么对于一个相遇节点u,答案为 dis(a, u) + dis(u, b) + 2n-2 - maxd(u)
dis(a, u) 我们可以树剖,maxd(u)我们可以换根dp,是可行的,然后可能要注意一下因为A先走B后走,dis(a, u) 和 dis(u, b) 我们注意一下corner case
不过我们注意到,最终答案的形式为:2n - 2 - maxd(u) + dis,这里的dis是二者在u相遇所需步数
我们考虑一开始 a 和 b 相向而行,那么 u 就是路径<a, b>中点mid,事实上以mid为相遇点就是最优解
为什么?
从mid开始往外枚举其它的u,maxd(u) 每次最多增加1,而dis 也会增加至少1,也就是说 最后的值单调不降,因而mid就是最优解
那么我们两次dfs就可以了
2、复杂度
时间复杂度: O(N)空间复杂度:O(N)
3、代码详解
#include <bits/stdc++.h>
// #define DEBUG
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
constexpr int inf32 = 1E9 + 7;
constexpr i64 inf64 = 1E18 + 7;
void solve() {
int n, a, b;
std::cin >> n >> a >> b;
-- a, -- b;
std::vector<std::vector<int>> adj(n);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> dis(n);
std::vector<int> path;
int mid = -1, d = 0;
auto dfs0 = [&](auto &&self, int u, int p) -> void{
path.push_back(u);
if (u == b) {
mid = path[(path.size() + 1) / 2 - 1];
d = path.size();
}
for (int v : adj[u])
if (v != p) {
self(self, v, u);
}
path.pop_back();
};
auto dfs1 = [&](auto &&self, int u, int p) -> void{
for (int v : adj[u])
if (v != p) {
dis[v] = dis[u] + 1;
self(self, v, u);
}
};
if (a == b) {
dfs1(dfs1, a, -1);
std::cout << 2 * n - 2 - *std::max_element(dis.begin(), dis.end()) << '\n';
return;
}
dfs0(dfs0, a, -1);
dfs1(dfs1, mid, -1);
std::cout << 2 * n - 2 - *std::max_element(dis.begin(), dis.end()) + d / 2 << '\n';
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
#ifdef DEBUG
int cur = clock();
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int t = 1;
std::cin >> t;
while (t--) {
solve();
}
#ifdef DEBUG
std::cerr << "run-time: " << clock() - cur << '\n';
#endif
return 0;
}