poj1095--Trees Made to Order(卡特兰数)

题目链接:点击打开链接

题目大意:按照图中给的编号方式,节点数逐渐增加,所有的点尽量在右子树上,问第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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值