Processing math: 100%

学习笔记 【笛卡尔树】

笛卡尔树

笛卡尔树是一种特定的二叉树,可由数列构造,在范围最值查询、范围 topk 查询 (rangetopkqueries)等问题上有广泛应用。它具有堆的有序性,中序遍历可以输出原数列。——摘自百度百科

笛卡尔树每一个结点由一个键值二元组 k 构成。要求 k 满足二叉搜索树的性质,而 v 满足堆的性质。如图:

笛卡尔树有这样的性质:

对于树上的任何一个节点 x 和左右儿子 lsrs 有:

  1. pos[ls]<pos[x]<pos[rs]
  2. val[x]<val[ls],val[rs] (大根堆时相反)。

即一棵笛卡尔树的 pos 满足二叉查找树, val 满足堆。

笛卡尔树的建树过程:

我们优先满足 pos 即按顺序依次插入,并调整树的结构,使之满足堆的性质。

所以我们要让新插入的点 x 在这棵树的最右面,沿着树根向右儿子走分两种情况:

  1. 这条链上 val 都比 val[x] 小(小根堆,大根堆相反),那么就让 x 作为最后一个点的右儿子。
  2. 找到了一个点 y val[y]>val[x] 此时为了满足堆的性质,y 必须作为 x 的儿子且必须是左儿子。

我们使用单调栈来维护笛卡尔树最右儿子链 val 的递增(递减)性。

建树过程的具体实现:

这样一棵笛卡尔树就建好了。

代码
#include<cstdio>
using namespace std;
typedef long long LL;
const int N=1e7+5;
int n;
int a[N];
int st[N],top;
int ls[N],rs[N];
inline void build(){
for(int i=1;i<=n;i++){
while(top&&a[st[top]]>a[i])
ls[i]=st[top--];
rs[st[top]]=i;
st[++top]=i;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build();
LL lans=0,rans=0;
for(int i=1;i<=n;i++)
lans^=(LL)i*(ls[i]+1),rans^=(LL)i*(rs[i]+1);
printf("%lld %lld",lans,rans);
return 0;
}

笛卡尔树的功能:

最简单的一个应用是求元素的左右延伸区间。根据笛卡尔树的性质, x 一定是 x 的子树中 val 最小的(小根堆)。这样我们再对树进行中序遍历,记录下 x 子树中最左最右的 pos 即可。

例题:

UVA1619 感觉不错 Feel Good
这是题解

SP1805 HISTOGRA - Largest Rectangle in a Histogram

P2659 美丽的序列 这是题解

AT2060 [AGC005B] Minimum Sum

posted @   Mr_think  阅读(202)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示