Description
We can number binary trees using the following scheme:
The empty tree is numbered 0. The single-node tree is numbered 1. All binary trees having m nodes have numbers less than all those having m+1 nodes. Any binary tree having m nodes with left and right subtrees L and R is numbered n such that all trees having m nodes numbered > n have either Left subtrees numbered higher than L, or A left subtree = L and a right subtree numbered higher than R. The first 10 binary trees and tree number 20 in this sequence are shown below: ![]() Your job for this problem is to output a binary tree when given its order number. Input
Input consists of multiple problem instances. Each instance consists of a single integer n, where 1 <= n <= 500,000,000. A value of n = 0 terminates input. (Note that this means you will never have to output the empty tree.)
Output
For each problem instance, you should output one line containing the tree corresponding to the order number for that instance. To print out the tree, use the following scheme:
A tree with no children should be output as X. A tree with left and right subtrees L and R should be output as (L’)X(R’), where L’ and R’ are the representations of L and R. If L is empty, just output X(R’). If R is empty, just output (L’)X. Sample Input 1 20 31117532 0 Sample Output X ((X)X(X))X (X(X(((X(X))X(X))X(X))))X(((X((X)X((X)X)))X)X) Hint Source |
二叉树的种数——卡特兰数。
这部分不是重点,重点是输出树的方法。原本脑子里硬想怎么也想不出,在纸上画来画去,今天拿出来一张白纸,平静心态,然后就像原本就在纸上一样,思路跟着笔就那么渐渐出来了。
首先是画了前几项,因为没有专业工具,所以凑合看看……
节点数为4的我没有画全。
具体的 对于当前节点,只要想办法找出左右子树的形态,然后递归画出来就行了。
图中每个编号的树表示方式是当前节点和左右子树形态编号。在图中指定编号往下画一定可以画出所求的树。
看图中Id较大的,比较明显可以看出来规律,当前树节点为x的话,当前节点的树中,第一棵左右子树节点数为0 x-1
然后就是以节点数为x-1的树形态来推,假设x-1的树形态个数为Ct[x-1],那么每加一次Ct[x-1],左子树形态就上升一次,直到左子树到达最终态。
左子树节点数
++
右子树节点数
−−
,然后接着分级增加,直到加到所求的Id,然后递归左右即可。
那么先考虑递归框架
void prt(LL x,bool f = 1)
{
if(!x) return;
if(f) putchar('(');
prt();
putchar('X');
prt();
if(f) putchar(')');
}
第一层传入参数 f = 0,不输出左右括号,其余节点,只要存在,那就要输出有右括号分界。
递归的结束x == 0 —->节点数为0
其余的,想办法找出左右形态 L R,然后画完左边,画当前’X’,再画右边就好了。
因为已知二叉树种数是卡特兰数,预处理一下即可。
填充完就出来最终代码了:
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <ctime>
#include <cstring>
#include <queue>
#include <stack>
#include <list>
#include <algorithm>
#include <map>
#include <set>
#define LL long long
#define Pr pair<int,int>
#define fread(ch) freopen(ch,"r",stdin)
#define fwrite(ch) freopen(ch,"w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f;
const int msz = 10000;
const int mod = 1e9+7;
const double eps = 1e-8;
LL ct[233];
LL cts[233];
int tp;
void init()
{
tp = 0;
ct[tp] = 1;
cts[tp++] = 0;
while(cts[tp-1] < 5000000000)
{
cts[tp] = cts[tp-1]+ct[tp-1];
ct[tp] = ct[tp-1]*(4*tp-2)/(tp+1);
tp++;
}
//for(int i = 0; i < tp; ++i)
// printf("%lld %lld\n",ct[i],cts[i]);
}
void prt(LL x,bool f = 1)
{
if(!x) return;
LL l,r;
int pos;
//找出当前子树节点个数。
for(int i = 0; ; ++i)
{
if(x < cts[i])
{
pos = i-1;
break;
}
}
//初始为 L:0 R:NodeNum-1
l = 0;
r = pos-1;
x -= cts[pos];
LL p1;
p1 = 0;
while(x-ct[r] >= 0)
{
x -= ct[r];
p1++;
if(p1 == ct[l])
{
p1 = 0;
l++,r--;
}
}
//printf("%lld-%lld\n",l,r);
//printf("%lld %lld\n",cts[l]+p1,cts[r]+x);
if(f) putchar('(');
prt(cts[l]+p1);
putchar('X');
prt(cts[r]+x);
if(f) putchar(')');
}
int main()
{
//fread("");
//fwrite("");
init();
LL x;
while(~scanf("%lld",&x) && x)
{
prt(x,0);
puts("");
}
return 0;
}