题目链接:点击查看

题目大意:给出n个节点的key和val,构造出其笛卡尔树的原型

笛卡尔树的定义:

所谓笛卡尔树,就是将给定的n个二元组(key,val)建成一棵树。使得:

  1. 如果只关注key,那么这是一棵二叉搜索树
  2. 如果只关注val,那么这是一个堆
  3. 对于任意三个节点fa,ls,rs,满足:
    1. key[ls]<key[fa]<key[rs]
    2. val[fa]>=max(val[ls],val[rs])

题目分析:首先我们需要先知道怎么建立笛卡尔树,我们可以按照key进行排序,经过排序之后,在最右边的树链上操作,对于每一个节点的val寻找一下该节点需要插入的位置,需要满足的条件是当前节点插入后的val值满足条件比其上面的节点要大,比起下面的节点要小,找到合适的位置后,将原本的链断开,将x插入,并将原本x之下的那条链连接到x的左子树上,这样我们就成功插入一个节点x了

先说一下为什么要这样插点,因为我们需要满足二叉搜索树的条件并且已经提前按照key值非降排好序了,所以在插入节点x之前,树中的所有key值都比节点x的key值要小,若我们只关注key的话,那么直接把点x插入到主根root节点向右遍历最下面的那个节点的右儿子即可,也就是最右端树链的末端,因为同时需要满足对于val值建立小顶堆的条件,所以我们需要在最右端的树链上寻找一个合适的位置插入节点x,当找到合适的位置后,这个位置肯定是满足了对于val值建堆的条件了,但却不满足对于key建二叉搜索树的条件了,所以我们需要稍微操作一下,因为当前x的key值在整棵树中是最大的,所以对于其父节点以及其祖先节点,肯定在右链的末端是合适的位置,但对于节点x下面的树链,也就是原本这个位置右链的下半部分来说,我们需要将这下半部分转移到当前节点x的左链上,这样就可以同时满足上述两个条件了

可能看起来比较难理解,可以自己动手画一下上面的这个过程,我也是稍微画了画图之后就豁然开朗了

现在说一下实现方法,对于key排序没什么好说的,现在就是对于每个点x需要找到点x插入的位置,这个位置肯定是从右链最下面开始,找到小于等于点x的第一个节点作为点x的父节点,我们该怎么快速找到这个父节点呢

网上的正解都是清一色的用单调栈来维护,确实,因为我们只需要在右链上寻找合适的位置,而且整棵树对于val满足堆的性质,所以从根部沿着右链到达叶子结点,这一整条树链实际上就是一个非严格递减的序列,这样一来我们就可以直接用单调栈来维护了,每次维护完之后栈顶就是我们需要寻找的节点编号了,注意,单调栈里维护的为每个节点的下标,也就是id,通过这个id就可以直接访问其内部成员了,用单调栈的话因为每个成员至多访问一次,所以时间复杂度也就是O(n)建树了

不过zx学长想出了另一个方法,也就是根据建树的整个过程得到启发的,因为每次我们找到一个放置点x的位置后,整个树链的下半部分都会拐个弯,拐到节点x的左节点去了,所以此时最右边的树链相当于将原本下半部分的节点删除掉了,然后此时的点x一定是最右端树链的最末端的节点,此时我们每次可以沿着最末端的节点往上跳,跳完之后直接将下半部分的树链拐弯,这样理论上每个节点至多也是遍历一次,时间复杂度是O(n)

其实两种方法大同小异,都离不开笛卡尔树的两个关键性质,理解了如何根据其性质建树之后就想怎么建就怎么建了。。

代码:

单调栈:

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e4+100;struct Node
{int l,r,fa,val,key,id;bool operator<(const Node& a)const{return key<a.key;}
}tree[N],ans[N];stack<int>s;//维护id,维护val的小顶栈 void insert(int x)
{while(s.size()&&tree[s.top()].val>tree[x].val)//单调栈维护非严格递增序列s.pop();tree[x].l=tree[s.top()].r;//更新 节点x 的左儿子tree[tree[s.top()].r].fa=x;//更新 节点x 的左儿子 的父节点tree[x].fa=s.top();//更新 节点x 的父节点tree[s.top()].r=x;//更新 节点x 的父节点 的右儿子s.push(x);
}void init()
{tree[0].val=-inf;tree[0].l=tree[0].r=0;s.push(0);
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);int n;while(scanf("%d",&n)!=EOF){init();for(int i=1;i<=n;i++){scanf("%d%d",&tree[i].key,&tree[i].val);tree[i].id=i;}sort(tree+1,tree+1+n);for(int i=1;i<=n;i++)insert(i);for(int i=1;i<=n;i++){ans[tree[i].id].fa=tree[tree[i].fa].id;ans[tree[i].id].l=tree[tree[i].l].id;ans[tree[i].id].r=tree[tree[i].r].id;}printf("YES\n");for(int i=1;i<=n;i++)printf("%d %d %d\n",ans[i].fa,ans[i].l,ans[i].r);}return 0;
}

暴跳父亲:

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=5e4+100;struct Node
{int l,r,fa,val,key,id;bool operator<(const Node& a)const{return key<a.key;}
}tree[N],ans[N];void insert(int pos)
{int cur=pos-1;while(tree[cur].val>tree[pos].val)//用while暴跳父亲,直到父亲节点的val小于等于当前节点的valcur=tree[cur].fa;tree[tree[cur].r].fa=pos;//更新 节点x 的左儿子 的父节点tree[pos].l=tree[cur].r;//更新 节点x 的左儿子tree[cur].r=pos;//更新 节点x 的父节点 的右儿子tree[pos].fa=cur;//更新节点x 的父节点
}void dfs(int x)
{if(!x)return;ans[tree[x].id].fa=tree[tree[x].fa].id;ans[tree[x].id].l=tree[tree[x].l].id;ans[tree[x].id].r=tree[tree[x].r].id;dfs(tree[x].l);dfs(tree[x].r);
}void init()
{tree[0].val=-inf;tree[0].l=tree[0].r=0;
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);int n;while(scanf("%d",&n)!=EOF){init();for(int i=1;i<=n;i++){scanf("%d%d",&tree[i].key,&tree[i].val);tree[i].id=i;}sort(tree+1,tree+1+n);for(int i=1;i<=n;i++)insert(i);dfs(tree[0].r);printf("YES\n");for(int i=1;i<=n;i++)printf("%d %d %d\n",ans[i].fa,ans[i].l,ans[i].r);}return 0;
}

POJ - 2201 Cartesian Tree(笛卡尔树-单调栈/暴跳父亲)相关推荐

  1. 笛卡尔树(超详细!!!)

    0x01 介绍 笛卡尔树是一种特定的二叉树,可由数列数列构造,在范围最值查询.范围top k查询(range top k queries)等问题上有广泛应用.它具有堆的有序性,中序遍历可以输出原数列. ...

  2. POJ - 2559 Largest Rectangle in a Histogram(笛卡尔树,单调栈实现)

    题目链接:点击查看 题目大意:给出一排高度不同,宽度都为 1 的矩形,问拼起来后最大的矩形面积是多少 题目分析:普通做法是用单调栈直接维护,我一直觉得单调栈处理这种矩形问题都比较抽象,也可能是我太菜了 ...

  3. 笛卡尔树(Cartesian Tree)

    一.简介 笛卡尔树是一种特定的二叉树数据结构,由数组存储.在范围最值.范围top k查询方面广泛应用. 笛卡尔树的性质: 树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列 树中节点 ...

  4. 牛客 - sequence(笛卡尔树+线段树)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数列 a 和数列 b ,求 题目分析:不算难的题目,对于每个 a[ i ] 求一下贡献然后维护最大值就好,具体思路就是,先找出每个 a[ i ] 左 ...

  5. [算法学习] 线段树,树状数组,数堆,笛卡尔树

    都是树的变种,用途不同 [线段树 Interval Tree] 区间管理,是一种平衡树 可看做是对一维数组的索引进行管理.一维数组不需要是排序好的 深度不超过logL 任一个区间(线段)都分成不超过2 ...

  6. 洛谷 - P4755 Beautiful Pair(笛卡尔树+主席树)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数列 a,现在一个数对 ( i , j ) 如果满足 a[ i ] * a[ j ] <=max( a[ i ] ~ a[ j ] ),则称其 ...

  7. 牛客多校3 - Sort the Strings Revision(笛卡尔树+分治)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数字串 s[ 0 ],每个位置的赋值初始时为 s[ i ] = i % 10 ( i ∈ [ 0 , n - 1 ] ),现在有一个长度为 n 的排 ...

  8. POJ1785(笛卡尔树的构造)

    题目:http://poj.org/problem?id=1785 题意:构造笛卡尔树,这里是最大堆构造,然后以广义表的形式输出这棵笛卡尔树. #include <iostream> #i ...

  9. 笛卡尔树详解带建树模板及例题运用(Largest Submatrix of All 1’s,洗车 Myjnie,Removing Blocks,SPOJ PERIODNI)

    文章目录 笛卡尔树 介绍 例题 Largest Submatrix of All 1's 应用 「POI2015」洗车 Myjnie [AGC028B] Removing Blocks SPOJ PE ...

最新文章

  1. 苹果开年第一购!买下仅24名员工的AI音乐公司,可根据环境动态生成音乐
  2. linux多线程求和_谈一谈C++中的多线程(上)
  3. linux sed 测试文件夹,测试开发笔记二(Linux与Shell脚本)
  4. 用RAII技术管理资源及其泛型实现
  5. pycharm可视化数据库
  6. 深圳学位分数计算机,深圳10区小一初一录取分数线汇总 附积分自测入口
  7. 解决Linux上解压tar.gz文件报错 (gzip: stdin: not in gzip format)
  8. Python网络编程、爬虫之requests模块使用
  9. 你不能强迫别人进步,跟别人没法强迫你进步一样
  10. mongodb安装及5安装studio 3t和studio3t破解
  11. 如何解决设备管理中的难点?
  12. QComboBox样式表
  13. 关键时刻救一命:旧手机改造求生工具
  14. 计算机外存储器有哪四个,计算机外存储器有哪些
  15. 粘贴时word左下角出现“正在与服务器联系以获取信息,按ESC取消”
  16. 小虾米闯江湖服务器维护中,小虾米闯江湖数据总结及中期注意事项一览
  17. Madgwick AHRS算法笔记
  18. JS做的一款动感超酷banner
  19. 美团-外卖骑手背后的AI技术
  20. 微信小程序开发的一些注意事项记录

热门文章

  1. php底层实现也是c语言,深入php内核,从底层c语言剖析php实现原理
  2. 服务器启动时的leader选举
  3. 数据库事务原理详解-事务基本概念
  4. 生成服务器证书的申请文件和私钥文件
  5. Spring核心系列之Spring中的事务
  6. Oracle 语言分类 数据类型 数据类型转换 常用函数 集合操作 子查询
  7. android 字符串下标,android – 在TextView中为特定字符设置下标和上标?
  8. mysql使用常量列_MySQL 执行计划
  9. java 不能反序列化_java中的序列化与反序列化
  10. 消控中心人员配置_关于2018年度环创中心楼宇物业综合管理考评情况的通报