Codeforces Round 947 (Div. 1 + Div. 2) D. Paint the Tree 题解 DFS

Paint the Tree

题目描述

378QAQ has a tree with n n n vertices. Initially, all vertices are white.

There are two chess pieces called P A P_A PA and P B P_B PB on the tree. P A P_A PA and P B P_B PB are initially located on vertices a a a and b b b respectively. In one step, 378QAQ will do the following in order:

  1. Move P A P_A PA to a neighboring vertex. If the target vertex is white, this vertex will be painted red.
  2. Move P B P_B PB to a neighboring vertex. If the target vertex is colored in red, this vertex will be painted blue.

Initially, the vertex a a a is painted red. If a = b a=b a=b, the vertex a a a is painted blue instead. Note that both the chess pieces must be moved in each step. Two pieces can be on the same vertex at any given time.

378QAQ wants to know the minimum number of steps to paint all vertices blue.

输入描述

Each test contains multiple test cases. The first line contains the number of test cases t t t ( 1 ≤ t ≤ 1 0 4 1\leq t\leq 10^4 1≤t≤104). The description of the test cases follows.

The first line of each test case contains one integer n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1\leq n\leq 2\cdot 10^5 1≤n≤2⋅105).

The second line of each test case contains two integers a a a and b b b ( 1 ≤ a , b ≤ n 1\leq a,b\leq n 1≤a,b≤n).

Then n − 1 n - 1 n−1 lines follow, each line contains two integers x i x_i xi and y i y_i yi ( 1 ≤ x i , y i ≤ n 1 \le x_i,y_i \le n 1≤xi,yi≤n), indicating an edge between vertices x i x_i xi and y i y_i yi. It is guaranteed that these edges form a tree.

It is guaranteed that the sum of n n n over all test cases does not exceed 2 ⋅ 1 0 5 2\cdot 10^5 2⋅105.

输出描述

For each test case, output the minimum number of steps to paint all vertices blue.

样例输入#1

复制代码
3
2
1 2
1 2
5
1 2
1 2
1 3
1 4
1 5
8
5 4
7 1
1 5
1 8
8 3
7 2
8 6
3 4

样例输出 #1

复制代码
2
8
13

原题

CF------传送门
洛谷------传送门

思路

先让棋子A与棋子B汇合,再让它们用最少的步数一起走过所有结点。这样的步数一定是最少的,因为将顶点涂成蓝色的前提是两棋子都经过某一点,而让两棋子相互靠近直到第一个结点被涂成蓝色便是最优的。值得注意的是,棋子A,B之间的相对位置存在两种情况:同步和异步。同步代表着两棋子可以在同一时间位于同一个结点上,使该结点变为蓝色;而异步代表着某一步中A棋子位于结点X上,只有在下一步时B棋子才有可能走到结点X上,并使该结点变为蓝色。

代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 2e5 + 6;
vector<int> e[maxn]; // e[i]存储以i为起点的所有有向边

void add(int x, int y) // 加无向边
{
    e[x].push_back(y);
    e[y].push_back(x);
}

int a, b;
deque<int> path; // 记录两颗棋子汇合的路径
int st = -1;     // 两颗棋子最快汇合后的点(也是dfs求max_dis的起点)
int step = 0;    // 汇合所需步数
int extra = 0;   // 两个棋子不可能位于同一个结点的情况(即两棋子异步),此时需要补充1步
void dfs1(int pos, int fa, int depth, int tag)
{
    if (st != -1) // 如果已找到答案,快速返回
        return;
    for (int i = 0; i < e[pos].size(); i++)
    {
        int to = e[pos][i];
        if (to == tag)
        {
            step = (depth + 1) / 2;
            if (path.size() % 2 == 0) // 两棋子异步(即两棋子初始时距离为奇数,也就是最短路径上的结点为偶数个的情况)
            {
                extra = 1;          // 补充1步
                path.push_front(a); // 特殊照顾一下两棋子初始时距离为1的情况,因为这种情况下st为a,但是path路径在dfs时并没有保存a结点
                st = path[step];
            }
            else // 两棋子同步(即两棋子初始时距离为偶数,也就是最短路径上的结点为奇数个的情况)
            {
                st = path[step - 1];
            }
            return;
        }
        if (to == fa)
            continue;
        path.push_back(to);
        dfs1(to, pos, depth + 1, tag);
        path.pop_back(); // 回溯
    }
}

int max_dis = 0; // 起点st到其他点的所有距离中的最远距离
void dfs2(int pos, int fa, int depth)
{
    max_dis = max(max_dis, depth); // 维护最远距离即可
    for (int i = 0; i < e[pos].size(); i++)
    {
        int to = e[pos][i];
        if (to == fa)
            continue;
        dfs2(to, pos, depth + 1);
    }
}

void solve()
{
    int n;
    cin >> n;
    cin >> a >> b;
    int x, y;
    for (int i = 1; i <= n - 1; i++)
    {
        cin >> x >> y;
        add(x, y);
    }
    if (a != b)
        dfs1(a, 0, 0, b);
    else
        st = a;
    dfs2(st, 0, 0);
    // 2 * (n - 1) - max_dis是用到了一个结论:从树中的一个结点开始走,走过树上所有结点至少需要2 * (n - 1) - max_dis步
    cout << step + 2 * (n - 1) - max_dis + extra << '\n';

    // 初始化,为了处理下一组测试数据
    for (int i = 1; i <= n; i++)
    {
        e[i].clear();
    }
    path.clear();
    st = -1;
    dis = 0;
    max_dis = 0;
    extra = 0;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    int t;
    cin >> t;
    while (t--)
        solve();

    return 0;
}
相关推荐
海绵宝龙13 小时前
Vue 中的 Diff 算法
前端·vue.js·算法
_Voosk13 小时前
macOS Xcode C++程序设置相对路径根目录
c语言·c++·xcode·swift
wadesir13 小时前
高效计算欧拉函数(Rust语言实现详解)
开发语言·算法·rust
aini_lovee13 小时前
基于扩展的增量流形学习算法IMM-ISOMAP的方案
算法
white-persist13 小时前
【内网运维】Netsh 全体系 + Windows 系统专属命令行指令大全
运维·数据结构·windows·python·算法·安全·正则表达式
Trouvaille ~13 小时前
【C++篇】智能指针详解(二):原理剖析与高级话题
服务器·c++·stl·资源管理·智能指针·编程规范·raii
超自然祈祷14 小时前
数据结构入门:图的基本操作、算法与 C++ 实现
算法·图搜索算法
蒙奇D索大14 小时前
【数据结构】排序算法精讲 | 快速排序全解:高效实现、性能评估、实战剖析
数据结构·笔记·学习·考研·算法·排序算法·改行学it
程序员良辰14 小时前
【算法新手入门】基本数据类型
算法
Blossom.11814 小时前
基于混合检索架构的RAG系统优化实践:从Baseline到生产级部署
人工智能·python·算法·chatgpt·ai作画·架构·自动化