题目描述
给一个有根二叉树,可以无限次的交换任意节点的左右子树,问最少交换多少次使得该树的中序遍历的字典序最小?
输入描述:
每个测试点有仅有一组数据
每组测试数据的一行有两个整数N,M,代表有N个节点,M为根节点。
接下来N行,每行两个整数ai,bi.ai表示第i个节点的左儿子,bi表示第i个节点的右儿子.
N∈[1,5×105]
ai,bi,M∈[1,N] 当ai,bi为0时 表示空节点.
输出描述:
输出两行
第一行 为最小交换次数.
第二行 为字典序最小的中序遍历.
示例1
输入
7 4 0 0 1 3 0 0 2 5 6 7 0 0 0 0
输出
0 1 2 3 4 6 5 7
题解
树形$dp$。
这题保证了每个数字都是不一样的,所以难度极小。记录$dp[i]$表示以$i$为根的子树中序遍历第一个数字的最小值,从下往上推一发就能知道哪些节点需要交换了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
int L[maxn], R[maxn];
int f[maxn], p[maxn];
int n, m;
int u[maxn], sz;
void dfs(int x) {
if(L[x] == 0 && R[x] == 0) {
p[x] = x;
return;
}
if(L[x]) dfs(L[x]);
if(R[x]) dfs(R[x]);
if(L[x] != 0 && R[x] != 0) {
if(p[L[x]] > p[R[x]]) {
f[x] = 1;
p[x] = p[R[x]];
} else {
p[x] = p[L[x]];
}
} else if(L[x] == 0) {
if(p[R[x]] < x) {
f[x] = 1;
p[x] = p[R[x]];
} else {
p[x] = x;
}
} else {
if(p[L[x]] > x) {
f[x] = 1;
p[x] = x;
} else {
p[x] = p[L[x]];
}
}
}
void work(int x) {
if(L[x]) work(L[x]);
u[sz ++] = x;
if(R[x]) work(R[x]);
}
int main() {
while(~scanf("%d%d", &n, &m)) {
for(int i = 1; i <= n; i ++) {
scanf("%d%d", &L[i], &R[i]);
f[i] = 0;
p[i] = 0;
}
dfs(m);
int ans = 0;
for(int i = 1; i <= n; i ++) {
ans = ans + f[i];
if(f[i]) swap(L[i], R[i]);
}
printf("%d\n", ans);
sz = 0;
work(m);
for(int i = 0; i < n; i ++) {
printf("%d", u[i]);
if(i < n - 1) printf(" ");
else printf("\n");
}
}
return 0;
}