笛卡尔树
笛卡尔树
前置知识:单调栈
基础知识
这里做点总结
笛卡尔树有两条性质
- 二叉搜索树
- 小根堆
定理:编号权值互不相同的笛卡尔树构造是唯一的
二叉搜索树满足
左儿子权值小于父节点,右儿子权值大于父节点
小根堆满足权值小于左右节点
(两个权值不能混为一谈)
因为笛卡尔树结点是由二元组(x,y)组成
x为二叉搜索树满足的权值,y为小根堆满足的权值
我们维护一个右链,即从根往右儿子走的链
插入时,我们从右链末端走,如果值小于末端的值,那就往上走,找到一个结点比插入值还小
如果有右子树,就把这个结点的右子树变为插入值的左子树,插入值变为这个结点的右二子
否则,直接变为右子树
如果找不到小于这个值的结点
则直接作为根
上述操作并未破坏笛卡尔树的性质,自我思考reason
这道题是维护小根堆
下面一道题是维护二叉树性质
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1e7+10;
int st[maxn],top=0;//st数组维护右链(从根往右儿子走),因为笛卡尔树也是一个二叉搜索树,所以右儿子一直比父节点大
int l[maxn],r[maxn];
int a[maxn];
long long ans1=0,ans2=0;
int read(){
int x=0,f=1;
char ch=getchar();
while(ch>'9' || ch<'0'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}int n;
int main(){
n=read();
for(int i=1;i<=n;++i){
a[i]=read();
int pos=top;
while(pos && a[st[pos]]>a[i]) --pos;//维护右链
if(pos) r[st[pos]]=i;//代替节点右二子
if(pos<top) l[i]=st[pos+1];//原本有右儿子换为插入节点的左儿子
st[top=++pos]=i;//入栈,保证单调递增
}
for(int i=1;i<=n;++i){
ans1^=(long long )i*(l[i]+1);
ans2^=(long long )i*(r[i]+1);
}cout<<ans1<<" "<<ans2<<endl;
return 0;
}
[TJOI2011]树的序
首先我们给定了二叉查找树的权值,但是如果就只是单纯的二叉查找树极有可能退化成链
但是会发现插入的顺序满足儿子节点比父节点晚(大),那么就满足小根堆,就可以构造笛卡尔树
先序遍历即可
定义一个结构体val为满足二叉树,id满足堆
struct node{
int val,id;
}a[]
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=1e5+10;
int st[maxn],top=0;
int n;
int l[maxn],r[maxn];
struct node{
int val,id;
}a[maxn];
bool cmp(node a,node b){
return a.val<b.val;
}
void dfs(int root){
if(!root)return ;
cout<<a[root].val<<" ";
dfs(l[root]);
dfs(r[root]);
}
int main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i].val,a[i].id=i;
sort(a+1,a+1+n,cmp); //按权值排序
for(int i=1;i<=n;++i){
int pos=top;
while(pos && a[st[pos]].id>a[i].id) --pos;
if(pos) r[st[pos]]=i;
if(pos<top) l[i]=st[pos+1];
st[top=++pos]=i;
}
dfs(st[1]);
return 0;
}
如果觉得讲的不顾透彻点这里