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;
}
相关推荐
语戚7 小时前
力扣 1621. 大小为K的不重叠线段的数目:动态规划(Java 实现)
java·算法·leetcode·动态规划·力扣·dp
青山木7 小时前
Hot 100 --- 最长有效括号
java·数据结构·算法·leetcode·动态规划
这料鬼有毒8 小时前
二刷hot100-1143.最长公共子序列
算法·leetcode·动态规划
政企项目老覃9 小时前
边缘 AI 推理部署:安防零售场景下的模型裁剪与端侧落地实践
人工智能·程序人生·算法·性能优化·vllm
I_belong_to_jesus9 小时前
std::unique_ptr成员函数用法
c++
Tairitsu_H10 小时前
[C++] 拷贝构造还是赋值重载?类默认成员函数细节详解
开发语言·c++·类和对象
GreenTea11 小时前
发布 3 天登顶 HN:不生成一个字的模型 Jev,我把它的源码和黑料都扒了一遍
前端·后端·算法
汉克老师11 小时前
GESP2026年9月认证C++四级( 第一部分选择题(1~7题)精讲
c++·gesp·小学生·学c++编程
geats人山人海11 小时前
现代c++第二章 字符串
c++
庖丁解牛11 小时前
无穷小不是一个“神秘小数”,而是一场奔向 0 的过程
算法