题目链接:点击打开链接
题目大意:按照图中给的编号方式,节点数逐渐增加,所有的点尽量在右子树上,问第m颗树是什么样子的,按照给的模式输出 (左)x(右),如果没有就缺省。
首先对于n个节点的二叉树一共有多少种形态,这刚好符合卡特兰数的值。
h(n) = C(2n,n)/(n+1) = C(2n,n) - C(2n,n-1) = h(n-1)*(4n-2)/(n+1) (递推公式)。按照递推公式就可以求出对于k个节点二叉树有多少种变化方式。
然后对于求第m颗树,可以先找到第m棵树应该是几个节点,然后找到是给节点数的第几种变化,然后深搜,按照先右后左的顺序,不断累加变化数,找到左右子树都应该是几个节点,第几种变化,然后深搜到下一层。
注意统计变化数的方式,一定是先右子树变化,然后左子树变化,右子树完成所有变化后,左子树的变化加一。
输出方式,按照左根右的输出方式输出。
测试样例:
42
(x(x))x(x(x))
43
(x(x))x((x)x)
44
((x)x)x(x(x))
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
#define LL __int64
#define max1 500000010
LL cat[30] , sum[30] ;
struct node{
int l , r ;
}p[200] ;
int cnt ;
void init() {
LL i ;
cat[0] = cat[1] = 1 ;
sum[1] = 1 ;
for(i = 2 ; i < 35 ; i++) {
cat[i] = cat[i-1]*(4*i-2)/(i+1) ;
sum[i] = sum[i-1] + cat[i] ;
if( sum[i] >= max1 ) break ;
}
return ;
}
void solve(int rt,int m,LL num) {
if( m == 0 ) return ;
int i , j , temp ;
LL ans = 0 , s ;
for(i = m ; i >= 0 ; i--) {
j = m - i ;
if( ans + cat[i]*cat[j] >= num) {
num -= ans ;
if( i ) {
p[rt].r = cnt ;
p[cnt].l = p[cnt].r = -1 ;
cnt++ ;
if( num%cat[i] ) temp = num%cat[i] ;
else temp = cat[i] ;
solve(cnt-1,i-1,temp) ;
}
if( j ) {
p[rt].l = cnt ;
p[cnt].l = p[cnt].r = -1 ;
cnt++ ;
if( num%cat[i] ) temp = num/cat[i]+1 ;
else temp = num/cat[i] ;
solve(cnt-1,j-1,temp) ;
}
break ;
}
ans += cat[i]*cat[j] ;
}
return ;
}
void dfs(int rt) {
if( p[rt].l != -1 ) {
printf("(") ;
dfs(p[rt].l) ;
printf(")") ;
}
printf("X") ;
if( p[rt].r != -1 ) {
printf("(") ;
dfs(p[rt].r) ;
printf(")") ;
}
}
int main() {
init() ;
int n , m , i ;
while( scanf("%I64d", &n) && n ) {
for(m = 1 ; m < 30 ; m++)
if( sum[m] >= n ) break ;
cnt = 0 ;
p[cnt].l = p[cnt].r = -1 ;
cnt++ ;
solve(0,m-1,n-sum[m-1]) ;
dfs(0) ;
printf("\n") ;
}
return 0;
}