当前位置: 首页 > ds >正文

[多彩数据结构] 笛卡尔树

[多彩数据结构] 笛卡尔树

定义

笛卡尔树,就是一棵树(废话)中存两个信息,为 ( w , i ) (w,i) (w,i)。其中 k = w e i g h t , i = i d k=weight,i=id k=weight,i=id

w w w 存的是节点的值, i i i 存的是编号。

每棵笛卡尔树都对应着一个排列,这点很重要(伏笔)。

下面给出一个标准的笛卡尔树。

  • w w w 满足堆的性质

这为我们后面操作提供了 log ⁡ n \log n logn 的时间复杂度。

  • i i i 满足二叉搜索树的性质。

啥意思呢?就是抽象下,在一维内,每个 i i i 都是从左到右递增。图:

画技高超

性质:

  • 若每个节点的 ( k , i ) (k,i) (k,i) 互不相同,那么整棵笛卡尔树也是独一无二的。

构建

  • 可以按照性质从根自上而下构建笛卡尔树。

因为之前说过:

每棵笛卡尔树都对应着一个排列,这点很重要(伏笔)。收回伏笔。

节点的编号满足排序树的性质,值满足堆的性质。

以下以小根堆举例子。

那么我们可以选取当前序列的最小值为根,并将序列划分为了两个子树。

左子树的 k k k 是小于根的 k k k 值的,右子树是大于根的 k k k 值的。(此乃二叉搜索树的性质)

如图。

然后,我们再慢慢处理子问题。

  • 由于 w w w 遵守堆的性质,那么我们就需要去找区间最值。啥办法?线段树?ST 表?树状数组?只想说:都太慢!!!!
如何插入?

不妨换个角度思考,按照编号由小到大构建笛卡尔树,此时需要在右边插入节点。

这时候就知道这东西的好处了吧 ↓ ↓ ↓ ↓ ↓ \downarrow \downarrow \downarrow \downarrow \downarrow ↓↓↓↓↓

在一维内,每个 i i i 都是从左到右递增

  • 如果新插入的节点的值是最大的,那么根据小根堆的性质,应当作为当前编号最大节点的右儿子。如 w = 9 w=9 w=9 的那位。

  • 否则,应当从当前按编号最大节点自底向上寻找合适的位置插入。

w = 6 w=6 w=6 的那位。

你给新节点找到一个合适位置安家的时候,应当满足什么性质呢?

  • 你的父亲节点的值刚好比它小
  • 你的儿子节点的值刚好比它大

这些也是堆和排序树的性质。

哦对,排序树好像就是二叉搜索树。

你一定会不停地往上找,直到满足以上条件。

无论到什么地方,靠下部分都只能作为虚拟节点的左子树,新节点爷只能作为靠上部分的右儿子。

多么通俗易懂。

因为编号只增不减,所以一定是上边的右儿子。另一个同理。

现在我们需要维护一个序列。它是从根开始不断像右儿子移动的序列。因为是从下往上跳,所以可以用栈 stack 来维护。

实现思路

  1. 按照编号顺序依次插入每个节点

​ 1.1.若栈顶节点的值还大于新节点

​ 1.1.1 将新节点的左儿子重置为栈顶节点

​ 1.1.2 删除栈顶节点,并重复1.1步骤

​ 1.2 将栈顶节点的右儿子重置为新节点

​ 1.3 将新节点入栈

看看是不是很像单调栈?

Code:

for(ljl i=1;i<=n;++i)//ljl = long long{cin>>node[i].v;while(!st.empty()/*栈不空*/&&node[st.top()].v>node[i].v/*没有找到合适位置*/)node[i].l=st.top(),st.pop();//操作1.1.1 and 1.1.2if(!st.empty())//这里如果没特判空栈貌似会炸吧。。node[st.top()].r=i;//操作1.2st.push(i);//1.3}

例题:

洛谷 P5854 【模板】笛卡尔树。

AC CODE:

#include<bits/stdc++.h>
using namespace std;
#define ljl long long//不开long long 见祖宗
const ljl N=1e7+5;
ljl n,ans1,ans2;
struct NODE{ljl v,l,r;
}node[N];
stack<int> st;
int main(){ios::sync_with_stdio(0);cin>>n;for(ljl i=1;i<=n;++i){cin>>node[i].v;while(!st.empty()&&node[st.top()].v>node[i].v)node[i].l=st.top(),st.pop();if(!st.empty())node[st.top()].r=i;st.push(i);}for(ljl i=1;i<=n;++i){ans1=ans1^(i*(node[i].l+1));ans2=ans2^(i*(node[i].r+1));}cout<<ans1<<' '<<ans2<<'\n';return 0;
}

坑点:因为数据量实在大( n ≤ 1 0 7 n\le 10^7 n107)用 cin 的佬们一定要关同步流。

(老师就栽过一次)

完结撒花!!!!

http://www.xdnf.cn/news/2801.html

相关文章:

  • 智能Python开发工具PyCharm v2025.1——AI层级功能重磅升级
  • Ajax 提交表单与文件上传
  • Windows 图形显示驱动-待机休眠优化
  • 升级Xcode16,flutter项目报错
  • 浏览器插件,提示:此扩展程序未遵循 Chrome 扩展程序的最佳实践,因此已无法再使用
  • jeecgboot 3.8.0 集成knife4j问题一文解决
  • MCP:如何通过模型控制推理助力AI模型实现“深度思考”?
  • 机器视觉的坐标标定
  • Python分支结构全面解析与实战应用指南
  • opendds编译开发(c#封装)
  • Android WebRTC回声消除
  • 具身智能:从理论突破到场景落地的全解析
  • 小目标检测的集成融合论文阅读
  • 项目实战-贪吃蛇大作战【补档】
  • 快速搭建对象存储服务 - Minio,并解决临时地址暴露ip、短链接请求改变浏览器地址等问题
  • 对比N+1查询和关联聚合查询
  • Spring Cloud Config 自定义配置源与动态刷新:从原理到企业级实践
  • Kafka 配置参数性能调优建议
  • 31、简要描述Promise.all的用途
  • 在 Ubuntu 22.04 x64 系统安装/卸载 1Panel 面板
  • 电子电器架构 ---电气/电子架构将在塑造未来出行方面发挥啥作用?
  • [Linux运维] [Ubuntu/Debian]在Lightsail Ubuntu服务器上安装Python环境的完整指南
  • 在线图书管理系统的结构化设计过程讲解
  • [密码学实战]SDF之设备管理类函数(一)
  • uniapp常用
  • case和字符串操作
  • 网络原理 - 10(HTTP/HTTPS - 1)
  • UniApp 实现分享功能
  • 深入探究C++ 中的stack、queue和deque
  • 图论---拓扑排序(DFS)