[TJOI2011]树的序(贪心,笛卡尔树)
[TJOI2011]树的序
题目描述
众所周知,二叉查找树的形态和键值的插入顺序密切相关。准确的讲:1、空树中加入一个键值k,则变为只有一个结点的二叉查找树,此结点的键值即为k;2、在非空树中插入一个键值k,若k小于其根的键值,则在其左子树中插入k,否则在其右子树中插入k。
我们将一棵二叉查找树的键值插入序列称为树的生成序列,现给出一个生成序列,求与其生成同样二叉查找树的所有生成序列中字典序最小的那个,其中,字典序关系是指对两个长度同为n的生成序列,先比较第一个插入键值,再比较第二个,依此类推。
输入输出格式
输入格式:
第一行,一个整数,n,表示二叉查找树的结点个数。第二行,有n个正整数,k1到kn,表示生成序列,简单起见,k1~kn为一个1到n的排列。
输出格式:
一行,n个正整数,为能够生成同样二叉查找数的所有生成序列中最小的。
输入输出样例
输入样例#1: 复制
4
1 3 4 2
输出样例#1: 复制
1 3 2 4
说明
对于20%的数据,n ≤ 10。
对于50%的数据,n ≤ 100。
对于100%的数据,n ≤ 100,000。
题解
先看出题目是直接建树然后贪心输出中序遍历。
然后交上去。发现如果树是链的话就被卡成\(O(n^2)\)。
怎么办呢?点开题解
发现题解多是笛卡尔树。
笛卡尔树是什么鬼东西(蒟蒻表示不会啊)
那就去学(抄题解)吧。
笛卡尔树类似于\(treap\)。
维护两个值,一个为\(key\)值即点权值,另一个在本题维护为\(id\),即出现顺序。
笛卡尔树是利用单调栈的特性建树的。
按样例的\(key\)值从小到大排序后。
\(id\)值为\(1,4,2,3.\)
我们先把\(1\)放进树。
然后让\(1\)点连右儿子\(key\)值为\(2\),\(id\)为\(4\)的点。
然后\(key\)值为\(3\)时,前面的\(2\)的\(id\)比3的\(id\)大。
即按中序遍历,\(3\)的左儿子是\(2\)。
于是就断开\(1\)连向\(2\)的边,然后连向\(3\),并让\(3\)向\(2\)连一条边作为左儿子。
建树的性质就是这样的。
\(O(n)\)建树。
代码
先上\(O(n^2)\)写法
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1000001;
struct node{
int ch[2],vi;
}t[N<<1];
int ch[N],n,cnt=1,ans[N],tot;
int read(){
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void build(int x){
int root=1;
while(t[root].ch[t[root].vi<ch[x]])root=t[root].ch[t[root].vi<ch[x]];
t[root].ch[t[root].vi<ch[x]]=++cnt;
t[cnt].vi=ch[x];
}
void dfs(int x){
ans[++tot]=t[x].vi;
if(t[x].ch[0])dfs(t[x].ch[0]);
if(t[x].ch[1])dfs(t[x].ch[1]);
}
int main(){
n=read();
for(int i=1;i<=n;i++)ch[i]=read();
t[1].vi=ch[1];
for(int i=2;i<=n;i++)build(i);
dfs(1);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}
AC代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1000010;
struct node{
int vi,ch[2],fa,id;
}ch[N],t[N];
int n,tot,line[N];
int read(){
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
bool cmp(node a,node b){
return a.vi<b.vi;
}
void add(int fa,int now,int f){
t[fa].ch[f]=now;
}
void dfs(int x){
if(!x)return;
printf("%d ",t[x].vi);
dfs(t[x].ch[0]);dfs(t[x].ch[1]);
}
int main(){
n=read();
for(int i=1;i<=n;i++){
ch[i].vi=read();ch[i].id=i;
}
sort(ch+1,ch+n+1,cmp);
for(int i=1;i<=n;i++){
int last=0;
while(tot&&t[line[tot]].id>ch[i].id)
last=tot--;
t[i].id=ch[i].id;t[i].vi=ch[i].vi;
add(line[tot],i,1);add(i,line[last],0);
line[++tot]=i;
}
dfs(t[0].ch[1]);
return 0;
}