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;
}
相关推荐
QXWZ_IA29 分钟前
**电力登高作业高空失保实时监管:千寻智能安全带高挂低用监测方案**
人工智能·科技·算法·能源·智能硬件
保持清醒54037 分钟前
类和对象上
c++
爱刷碗的苏泓舒41 分钟前
FFRT:固定失败率比率检验、宽巷模糊度固定及 Ratio Test
算法·相位偏差·模糊度固定·ppp-ar·ffrt·ratio test·wlnl
小园子的小菜1 小时前
深入理解 JVM 垃圾回收:从对象判定、回收算法到经典收集器全解析
jvm·算法
hold?fish:palm1 小时前
7 接雨水
开发语言·c++·leetcode
2601_954526751 小时前
【硬核长文】从卡门涡街物理方程到边缘网关温压补偿算法:工业蒸汽测控实战,深度解密靠谱的涡街流量计厂家有哪些
算法
吞下星星的少年·-·1 小时前
牛客技能树:区间翻转
算法·滑动窗口
流浪0012 小时前
数据结构篇(五):线性表——栈
数据结构·c++·算法
郝学胜-神的一滴2 小时前
中级OpenGL教程 022:探秘三维世界的血脉传承——物体父子关系与矩阵递归奥义
c++·线性代数·unity·矩阵·游戏引擎·unreal engine·opengl
tkevinjd2 小时前
力扣148-排序链表
算法·leetcode·链表