题意:
分析
我们根据题意分析,设节点数为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;
}