题目描述
给一个有根二叉树,可以无限次的交换任意节点的左右子树,问最少交换多少次使得该树的中序遍历的字典序最小?
输入描述:
每个测试点有仅有一组数据
每组测试数据的一行有两个整数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 ", ans); sz = 0; work(m); for(int i = 0; i < n; i ++) { printf("%d", u[i]); if(i < n - 1) printf(" "); else printf(" "); } } return 0; }