[BZOJ3393]二叉树

30 篇文章 0 订阅
10 篇文章 0 订阅
题目描述
给定一棵二叉树,节点标号从1到n(n≤100000)。在不改变其中序遍历的情况下,请改变树的结构,使得这棵二叉树的先序遍历(前序遍历)字典序最小。

输入
第一行一个整数n,表示二叉树的节点数。
接下来n行,每行两个整数。第i行的两个整数表示编号为i的节点的左儿子和右儿子的编号(不存在即为0)。

输出
输出一行n个整数,表示不改变中序遍历的情况下字典序最小的前序遍历序列。

样例输入
5
5 4
0 0
2 1
0 0
0 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值