題目描述給定一棵二叉樹,節點標號從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]二叉樹
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.