题目描述给定一棵二叉树,节点标号从1到n(n≤100000)。在不改变其中序遍历的情况下,请改变树的结构,使得这棵二叉树的先序遍历(前序遍历)字典序最小。
输入第一行一个整数n,表示二叉树的节点数。接下来n行,每行两个整数。第i行的两个整数表示编号为i的节点的左儿子和右儿子的编号(不存在即为0)。
输出输出一行n个整数,表示不改变中序遍历的情况下字典序最小的前序遍历序列。
样例输入55 40 02 10 00 0
样例输出1 2 3 5 4
来源 by azui
题解:贪心+分治。首先处理出树的中序,用数组存起来。然后每次选出当前区间中的最小值,当作根,再递归按上述方法处理其左右区间。因为二叉树中序的性质,数组中一个数的左右区间就是它的左子树和右子树。
唯一的问题就是如何快速找出一段区间中的最小值。这里用的是st表,也可以用线段树维护。#include<cstdio> const int N=1e5+10; const int LOG=25; int n, rot=1, fa[N], son[N][2]; void Root( int &x ) { while( fa[x] ) x=fa[x]; } int st[N][LOG], lg2[N], note[N], cnt; #define Cmp( i,j ) ( note[i]<note[j] ? i : j ) void DFS( int r ) { if( !r ) return; DFS( son[r][0] ); note[++cnt]=r; st[cnt][0]=cnt; DFS( son[r][1] ); } void Pre() { for( int i=2; i<=n; i++ ) lg2[i]=lg2[i>>1]+1; for( int j=1; j<=lg2[n]; j++ ) for( int i=1; i+(1<<j)-1<=n; i++ ) st[i][j]=Cmp( st[i][j-1], st[ i+(1<<(j-1)) ][j-1] ); } void Print( int l, int r ) { if( l>r ) return; int k=lg2[r-l+1]; int w=Cmp( st[l][k], st[ r-(1<<k)+1 ][k] ); printf( "%d", note[w] ); cnt++; cnt<n ? putchar(32) : putchar(10); Print( l, w-1 ); Print( w+1, r ); } int main() { scanf( "%d", &n ); for( int i=1; i<=n; i++ ) { scanf( "%d%d", &son[i][0], &son[i][1] ); fa[ son[i][0] ]=fa[ son[i][1] ]=i; } Root( rot ); DFS( rot ); Pre(); cnt=0; Print( 1, n ); return 0; }
[BZOJ3393]二叉树
最新推荐文章于 2020-02-26 12:58:22 发布