目录
一、题目
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;
}




















