[BZOJ3393]二叉樹

題目描述
給定一棵二叉樹,節點標號從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;
}


發佈了101 篇原創文章 · 獲贊 7 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章