CF2071C Trapmigiano Reggiano

给定一棵 \(n\) 个节点的树,起点 \(st\) 和终点 \(en\)。要求一个排列 \(p_i\),使得从 \(st\) 出发,第 \(i\) 步向 \(p_i\) 方向走一步(顺着当前位置 \(u\)\(p_i\) 的简单路径走一步,\(u=p_i\) 时不移动),\(n\) 步后能恰好走到 \(en\)。中途可以经过 \(en\)

\(st\) 出发的做法不好实现,以 \(en\) 为根考虑。具体做法是,以 \(en\) 为根 dfs 统计各个节点的深度,把 \(n\) 个节点按照深度从大到小输出即可(同一层顺序随意)。这是基于一个归纳:

  • 如果深度 \(>k\) 的节点都被操作完了,则当前位置 \(u\) 的深度一定 \(\le k+1\)(假设);
  • 现在选取一个深度为 \(k\)(深度最大)的节点 \(p_i\),向它走一步。
    • 如果 \(u=p_i\),则不动,深度为 \(k\)
    • 如果 \(u\)\(p_i\) 的祖先,向下走一步,深度 \(\le k\)
    • 否则 \(u\) 向上走一步,深度 \(\le k\)

所以操作完深度为 \(k\) 的所有节点后,\(u\) 深度 \(\le k\)。操作完深度为 \(1\) 的节点后,\(u\) 的深度为 \(1\),到达 \(en\)。这也说明了一定有解,不会输出 -1

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int n, st, en, dep[N];
vector<int> e[N], dis[N];

void dfs(int u, int fa) {
    dep[u] = dep[fa] + 1;
    dis[dep[u]].push_back(u);
    for (int v : e[u]) {
        if (v != fa) dfs(v, u);
    }
}

void solve() {
    scanf("%d%d%d", &n, &st, &en);
    for (int i = 1; i < n; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(en, 0);
    for (int i = n; i; i--) {
        for (int j : dis[i]) {
            printf("%d ", j);
        }
    }
    putchar('\n');
    for (int i = 1; i <= n; i++) {
        e[i].clear();
        dis[i].clear();
    }
}
posted @ 2025-03-25 09:21  XYukari  阅读(12)  评论(0)    收藏  举报