「清华集训 2017」小 Y 和二叉树
首先,我们贪心地选一个点来作为中序遍历的第一个点,也就是说这个点不可能有左子树。
显然符合条件的点的度数小于 \(3\)。
然后我们就考虑从所选的这个点 \(dfs\),捋清最后的二叉树中节点之间的父子关系。
考虑 \(dfs\) 的过程(记当前点为 \(u\)):
如果 \(u\) 是由它左下方的点向上走得到的,并且 \(u\) 还连出去两个点 \(x, y\),那么我们就要考虑把哪个点作为 \(u\) 的右儿子,哪个点作为 \(u\) 的父亲。
我们还是贪心地想,显然我们要把编号越小越好的点在中序遍历中放在 \(u\) 的右边的位置,如果我们用 \(mn_i\) 表示连向 \(i\) 的联通块中,可以作为这一段在中序遍历中的第一个的编号最小的点,那么我们肯定要选择 \(x, y\) 中 \(mn\) 值较小的点来作为 \(u\) 的右儿子,另一个作为父亲,因为右儿子是会先排入中序遍历当中的。
类似地,如果只连出去唯一的一个 \(x\),我们就把 \(mn_x\) 和 \(u\) 本身的编号作比较,考虑把 \(x\) 作为父亲还是右儿子。
如果没有点了的话,当前的 \(u\) 显然就会作为二叉树最后的根。
有了以上的思路,对于 \(u\) 从它上方的点连下来的情况也就不难讨论了。
最后再跑一遍中序遍历输出方案就好了。
参考代码:
#include <cstring>
#include <cstdio>
#include <cmath>
int min(int a, int b) { return a < b ? a : b; }
void swap(int& a, int& b) { int t = a; a = b; b = t; }
template < class T > void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
const int _ = 1e6 + 5;
int n, dgr[_], G[_][3], ch[2][_], st, root, mn[_];
void dfs_mn(int u, int f) {
for (int i = 0; i < 3; ++i) {
int v = G[u][i]; if (!v || v == f) continue ;
dfs_mn(v, u), mn[u] = min(mn[u], mn[v]);
}
}
void dfs(int u, int f, int opt) {
int x = 0, y = 0;
for (int i = 0; i < 3; ++i)
if (G[u][i] && G[u][i] != f) {
if (!x) x = G[u][i]; else if (!y) y = G[u][i];
}
if (opt == 0) {
ch[0][u] = f;
if (x && y) {
if (mn[x] > mn[y]) swap(x, y); dfs(x, u, 1), dfs(y, u, 0);
}
else if (x + y) { x = x + y;
if (mn[x] < x) root = u, dfs(x, u, 1); else dfs(x, u, 0);
} else root = u;
} else {
if (opt == 1) ch[1][f] = u;
if (opt == 2) ch[0][f] = u;
if (x && y) {
if (mn[x] > mn[y]) swap(x, y); dfs(x, u, 2), dfs(y, u, 1);
} else if (x + y) { x = x + y;
if (mn[x] > u) dfs(x, u, 1); else dfs(x, u, 2);
}
}
}
void Dfs(int x) { if (x) Dfs(ch[0][x]), printf("%d ", x), Dfs(ch[1][x]); }
int main() {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
read(n);
memset(mn, 0x3f, sizeof mn);
for (int i = 1; i <= n; ++i) {
read(dgr[i]);
for (int j = 0; j < dgr[i]; ++j) read(G[i][j]);
if (dgr[i] <= 2) { mn[i] = i; if (!st) st = i; }
}
dfs_mn(st, 0), dfs(st, 0, 0), Dfs(root);
return 0;
}