【算法学习】笛卡尔树
前言
顺着思路看下去还是很简单的,我只列出代码展示。
我现在肯定不会这么说了,虽然考这个的概率还是很小但还是要记录的(学习是给自己学的,笔记是给自己记的)
笛卡尔树要满足堆和二叉搜索树的两个性质,而且他有两种键值 \(w,k\),分别是根节点大于子节点,左节点小于根节点,根节点小于右节点。
竞赛中使用笛卡尔树时,常用数组下标作为二元组的键值 \(k\)。
真的讲解
我们将使用栈构建树,我们始终维护一条右链,当插入一个点,在链上找到第一个w小于该节点的节点,将该节点的右子树接在新点的左子树上,新点成为新右链的最后一个点,过程正如 oi-wiki 一样。
然后上代码,感觉比 wiki 还简单明显。
s[1]=1;
int top=1;
for(int i=2;i<=n;i++){
while(a[s[top]]>a[i]&&top){
top--;
}
if(!top){
l[i]=s[top+1];
}
else{
l[i]=r[s[top]];
r[s[top]]=i;
}
s[++top]=i;
}
题目练习
P5854 【模板】笛卡尔树
就是一个板子,按上面说的写就可以,我就不出代码你能怎样,自己写!!!
P1377 [TJOI2011] 树的序
这题需要读懂题意才可以,发现就是个构造笛卡尔树的题,但是好像又不是模板题,怎么办,我们将序列排序使得字典序最小,然后我们按照位置id加点使得不破坏树的插入顺序,每次加入点和笛卡尔树一样放左接右,因为我们是按id加点,所以应该早加的终究会在上方,只不过在过程中使得能在上方加在左子树的尽量在上。
点击查看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
#define int ll
int n;
int l[N];
int r[N];
int s[N];
struct ss{
int a,id;
}a[N];
void dfs(int x){
if(x==0){
return;
}
cout<<a[x].a<<" ";
dfs(l[x]);
dfs(r[x]);
}
bool cmp(ss g,ss h){
return g.a<h.a;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].a;
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
int top=0;
for(int i=1;i<=n;i++){
while(a[s[top]].id>a[i].id&&top){
top--;
}
l[i]=r[s[top]];
r[s[top]]=i;
s[++top]=i;
}
dfs(r[0]);
return 0;
}