HDU 1100 Trees Made to Order


题意:

分析

我们根据题意分析,设节点数为n的二叉树有h(n)个,考虑根节点下的左子树和右子树,他 们的节点数分别为(0,n-1); (1,n-2);。。。;(n-1,0),所以h(n) = h(0)*h(n-1)+h(1)*h(n-2)+...+h(n-1)*h(0), 且h(0)= 1, h(1)=1, 和卡特兰数的初始值与递推关系相符,所以我们得出h(n)就是个卡特兰数。

有了上述的结论,我们就可来定位x对应的节点数。我们首先生成一个卡特兰数的序列h(n),再生成卡特兰数的前n项和的序列s(n),给定 一个x,我们可以找到这样一个n,让是s(n-1)< x <= s(n),从而得出这棵树是n个节点的。再用x - h(n-1)的到它在h(n)中位置。因为x上限为500,000,000,所以我们很容易知道,n最大为18.

接下来,对于n个节点的树,我们已知它有h(n)个,我们再来对这h(n)棵树进行从1到h(n)排位,根据规则,从左边子树的个数高的排位高,我们先来分段,根据公式
h(n) = h(0)*h(n-1)+h(1)*h(n-2)+...+h(n-1)*h(0)
我们先利用x - h(n-1)来判断它在那一段,即它对应哪个h(x)*h(y), 这样就能知道他的左边子树的节点数为x,右边的为y。

第三步,我们来算它在h(x)*h(y)中的位置,这很容易得到,设为pos。再根据规则,知道左边子树的位置为(pos + h(y) -1)/h(y),右边位置为 pos mod h(y),但是记住,结果为零时,要变成h(y),有了这些信息,我们就能以节点数和pos为参数递归的运算下去,知道节点数为1停止。最后把树木打印出来就是。

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define PB push_back

typedef long long ll;

const int maxn=500000000;
vector<ll> sum,tree;

void init() {
    sum.PB(0);
    tree.PB(1);
    while(*sum.rbegin()<maxn) {
        tree.PB(0);
        for(int i=0;i<tree.size()-1;i++)
            *tree.rbegin()+=tree[i]*tree[tree.size()-i-2];
        sum.PB(*sum.rbegin()+*tree.rbegin());
    }
}

void dfs(int p) {
    if(!p)
        return ;
    int m=lower_bound(sum.begin(),sum.end(),p)-sum.begin();
    p-=sum[m-1];//该棵树在相同节点数的树中的序数
    int k=0,lp=1,rp;
    while(p>tree[m-1-k]*tree[k]&&m-1-k-1>=0) {
        p-=tree[m-1-k]*tree[k];
        k++;
    }//求左子树节点数
    if(k) {
        while(p>tree[m-1-k]) {
            p-=tree[m-1-k];
            lp++;
        }
        lp+=sum[k-1];//左子树序数
        printf("(");
        dfs(lp);
        printf(")");
    }
    printf("X");
    if(m-1-k) {
        rp=p+sum[m-1-k-1];//右子树序数
        printf("(");
        dfs(rp);
        printf(")");
    }
}

int main() {
    init();
    int n;
    while(cin >> n,n) {
        dfs(n);
        cout << endl;
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值