给定一棵 \(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();}
}