题目描述
我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串。FBI树是一种二叉树,它的结点类型也包括F结点,B结点和I结点三种。由一个长度为2N的“01”串S可以构造出一棵FBI树T,递归的构造方法如下:
- T的根结点为R,其类型与串S的类型相同;
- 若串S的长度大于1,将串S从中间分开,分为等长的左右子串S1和S2;由左子串S1构造R的左子树T1,由右子串S2构造R的右子树T2。
现在给定一个长度为2N的“01”串,请用上述构造方法构造出一棵FBI树,并输出它的后序遍历[2]序列。
具体样例的树结构如下:
输入输出格式
输入的第一行是一个整数N(0 ≤ N ≤ 10),第二行是一个长度为2^N的“01”串。
输出包括一行,这一行只包含一个字符串,即FBI树的后序遍历序列。
样例输入
3
10001011
样例输出
IBFBBBFIBFIIIFF
时间限制
1s
注释 Hint
对于40%的数据,N≤ 2;
对于全部的数据,N≤10。
[1] 二叉树:二叉树是结点的有限集合,这个集合或为空集,或由一个根结点和两棵不相交的二叉树组成。这两棵不相交的二叉树分别称为这个根结点的左子树和右子树。
[2] 后序遍历:后序遍历是深度优先遍历二叉树的一种方法,它的递归定义是:先后序遍历左子树,再后序遍历右子树,最后访问根。
本来在洛谷上看到这道题的时候是挺懵的,毕竟我图这边的知识非常薄弱。
样例都不知道怎么来的,还又去确定了一下后序遍历是左右根。
然后发现校OJ上有这道题样例的构造树
太开心了,一下就明白了。
然后发现和加分二叉树有点像。
题目保证串长是2n,所以这个结构就和线段树是一样的。
我们用root[l][r]表示l-r的根,每次搜出左右子节点的根,如果左等右,根就等于左(右)如果不同就是’F’。
如果找到叶节点我们就根据读入的串来判断’I’和’B’
然后输出的时候根据后序遍历,先输出左右子树,再输出根。
#include<bits/stdc++.h>
#define ll long long
#define MAXN 5000100
#define N 1001
#define INF 0x3f3f3f3f
#define gtc() getchar()
using namespace std;
template <class T>
inline void read(T &s){
s = 0; T w = 1, ch = gtc();
while(!isdigit(ch)){if(ch == '-') w = -1; ch = gtc();}
while(isdigit(ch)){s = s * 10 + ch - '0'; ch = gtc();}
s *= w;
}
template <class T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar(x % 10 + '0');
}
int n;
int m = 1;
char a[MAXN];
char root[1500][1500];
void dfs(int l, int r){
if(l == r){
root[l][r] = a[l] == '1' ? 'I' : 'B'; return ;
}
int mid = (l + r) >> 1;
dfs(l, mid), dfs(mid + 1, r);
root[l][r] = root[l][mid] == root[mid+1][r] ? root[l][mid] : 'F';
}
void out(int l, int r){
if(l == r){
cout << root[l][r]; return ;
}
int mid = (l + r) >> 1;
out(l, mid), out(mid+1, r);
cout << root[l][r];
}
int main()
{
read(n);
for(int i = 1; i <= n; ++i) m *= 2;
scanf("%s", a+1);
dfs(1, m);
out(1, m);
return 0;
}