[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;
}
posted @ 2018-09-11 11:02  Epiphyllum_thief  阅读(265)  评论(0编辑  收藏  举报