笛卡尔树
笛卡尔树
介绍
笛卡尔树是形如上图的一棵树,满足
①堆的性质,如本图,小根堆,儿子的值大于等于父亲的值
②二叉搜索树性质,即左子树的点key(默认为下标)比根小,右子树的点key(默认为下标)比根大
显然,按中序遍历这棵树,可得原序列
和treap不同的是,树高不保证是log
③询问下标i到下标j之间(i<j)的最小值,只需寻找[i,j]的lca
构建
每次插入一个新节点时,为了确保二叉搜索树的性质得到满足(别忘了我们按照 key顺序插入),我们需要将新节点插入到尽可能靠右端的位置。
我们维护最右链,同时,注意到最右链中val单调
因此可以维护一个单调栈,来即时地找到插入位置
模板
#include <iostream>
#include <utility>
#include <cstdio>
#include <cctype>
using namespace std;
const int N=1e7+5;
inline int read() {
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
int n,dat[N],st[N],ls[N],rs[N],top;
inline void insert(int x) {
while(top&&dat[st[top]]>dat[x]) top--;
ls[x]=rs[st[top]],rs[st[top]]=x;
st[++top]=x;
}
long long ans1,ans2;
int main() {
n=read();
for(int i=1;i<=n;i++) dat[i]=read();
for(int i=1;i<=n;i++) insert(i);
for(int i=1;i<=n;i++)
ans1^=(i*(ls[i]+1ll)),ans2^=(i*(rs[i]+1ll));
printf("%lld %lld\n",ans1,ans2);
return 0;
}
建树的方法2
每次选取区间最大值作为根,然后往两边递归也可以建树
直接暴力是O(n^2)
线段树优化一下O(nlogn)
树的序
Description
给一个生成序列,建出一棵笛卡尔树,求字典序最小的可以得到相同笛卡尔树的生成序列
Solution
按题意建好笛卡尔树之后输出先序遍历即可
建树注意题目中的序列是模板中的key 序列,所以输入这里稍微转换一下
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100005;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,dat[N],st[N],top,ls[N],rs[N];
void insert(int x) {
while(top&&dat[st[top]]>dat[x]) top--;
ls[x]=rs[st[top]];rs[st[top]]=x;
st[++top]=x;
}
void dfs(int x) {
if(!x) return;
printf("%d ",x);
dfs(ls[x]);
dfs(rs[x]);
}
int main() {
n=read();
for(int i=1,x;i<=n;i++) x=read(),dat[x]=i;
for(int i=1;i<=n;i++) insert(i);
dfs(st[1]);
return 0;
}
Periodni ——题解https://www.luogu.com.cn/blog/Alansp/solution-sp3734
https://www.luogu.com.cn/problem/P3246
https://www.luogu.com.cn/problem/P5044
https://www.luogu.com.cn/problem/P4755
http://codeforces.com/problemset/problem/1117/G
https://www.luogu.com.cn/problem/P2611
参考文章: