P2596 [ZJOI2006]书架

无旋treap可以维护一棵树的中序遍历结果.但是不支持通过编号来找节点.于是在无旋treap的基础上,我维护了每个节点的父亲,这样就可以求出一个节点是中序遍历中的第几个.
那么对于一个节点,每次将它向树根跳,如果它是右儿子,那么就将它父亲的左子树的值以及父亲的大小计入结果.
那么问题就只有如何记录父亲了.显然会改变父亲的只有 split和merge操作,那么我只需要在这两个函数中修改就可以了.在
split的时候再传两个参数记录父亲, merge在修改儿子的时候同时将父亲一起修改. 其他的都是无旋treap的基本操作了.
Top:提取该节点,放在树的最前面合并.
Bottom: 提取节点,放在树的最后面合并.
Insert:将它与前驱/后继从整棵树中分离出来,交换顺序合并.
Ask: 直接通过编号找到节点是中序遍历结果第几个.
Query:先找到节点是中序遍历第几个,然后split前k个,在分离出的第一颗子树中找最右边的节点.
题解来源

注意:fhq-treap中所有的排名实际上都指的是前面有多少个元素(前面有多少本书,例如实际排名为1,那么查询用的排名为0),而不是实际的排名

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cstdlib>
#include<ctime>using namespace std;
typedef long long ll;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;//num:节点编号
namespace treap{// 无旋treap (fhq-treap)const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;struct fhq_treap{int l, r;int size;int fa;int val, fix;ll sum;}tr[N];int r1, r2, r3, r4;int cnt;int x, y, z, root;int id[N];inline void pushup(int p){tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1;//tr[p].sum = tr[tr[p].l].sum + tr[tr[p].r].sum + tr[p].val;//tr[tr[p].l].fa = tr[tr[p].r].fa = p;}inline void split(int p, int k, int &x, int &y, int fx = 0, int fy = 0){//传两个父亲节点的参数是为了防止记录父亲出问题.//因为父亲记录儿子的时候是通过取地址传参修改的//而两颗子树记录的父亲是分开的if(!p){x = y = 0; return ;}if(k <= tr[tr[p].l].size)tr[p].fa = fy, y = p, split(tr[p].l, k, x, tr[p].l, fx, p), pushup(p);else tr[p].fa = fx, x = p, split(tr[p].r, k - tr[tr[p].l].size - 1, tr[p].r, y, p, fy), pushup(p);}inline int merge(int x, int y){if(!x || !y)return x + y;if(tr[x].fix < tr[y].fix){//小根堆tr[x].r = merge(tr[x].r, y);tr[tr[x].r].fa = x;pushup(x);return x;}else {tr[y].l = merge(x, tr[y].l);tr[tr[y].l].fa = y;pushup(y);return y;}}//!得到新节点inline int get_node(int val){tr[++ cnt].size = 1;tr[cnt].fix = rand();tr[cnt].val = val;tr[cnt].fa = 0;id[val] = cnt;return cnt;}//!树中插入新节点inline void my_insert(int k, int val){split(root, k, x, y);root = merge(merge(x, get_node(val)), y);}int get_rank_by_num(int p){int res = tr[tr[p].l].size + 1;int node = p;while(node != root && p){if(p == tr[tr[p].fa].r)res += tr[tr[tr[p].fa].l].size + 1;p = tr[p].fa;}return res;}//!查找节点的祖先节点inline int get_anc(int x){while(tr[x].fa){x = tr[x].fa;}return x;}inline void main(){srand((unsigned)time(NULL));memset(tr, 0, sizeof tr);cnt = 0;}inline void solve_T(int x){x = get_rank_by_num(id[x]);split(root, x, r1, r3);split(r1, x - 1, r1, r2);root = merge(r2, merge(r1, r3));}void solve_B(int x){x = get_rank_by_num(id[x]);split(root, x, r1, r3, 0);split(r1, x - 1, r1, r2, 0);root = merge(r1, merge(r3, r2));}void solve_I(int x, int y){x = get_rank_by_num(id[x]);if(y == 0)return ;if(y > 0){split(root, x + 1, r3, r4);split(r3, x, r2, r3);split(r2, x - 1, r1, r2);root = merge(r1, merge(r3, merge(r2, r4)));}else {split(root, x, r3, r4);split(r3, x - 1, r2, r3);split(r2, x - 2, r1, r2);root = merge(r1, merge(r3, merge(r2, r4)));}}void solve_A(int x){x = get_rank_by_num(id[x]);printf("%d\n", x - 1);}void solve_Q(int x){//先找到节点是中序遍历第几个,然后split前k个,//在分离出的第一颗子树中找最右边的节点.split(root, x, r1, r2);int node = r1;while(tr[node].r) node = tr[node].r;printf("%d\n", tr[node].val);root = merge(r1, r2);}
}int n, m;
int a[N];int main()
{scanf("%d%d", &n, &m);treap::main();for(int i = 1; i <= n; ++ i)scanf("%d", &a[i]), treap::my_insert(i - 1, a[i]);for(int i = 1; i <= m; ++ i){char op[10];int x, y;scanf("%s%d", op, &x);if(op[0] == 'T'){treap::solve_T(x);}else if(op[0] == 'B'){treap::solve_B(x);}else if(op[0] == 'I'){scanf("%d", &y);treap::solve_I(x, y);}else if(op[0] == 'A'){treap::solve_A(x);}else if(op[0] == 'Q'){treap::solve_Q(x);}}return 0;
}

luogu P2596 [ZJOI2006]书架(平衡树、无旋treap(按排名分裂)一些更复杂的操作)相关推荐

  1. P2596 [ZJOI2006]书架 无旋treap 按照排名分裂

    传送门 文章目录 题意: 思路: 题意: 实现如下操作: 思路: fhq-treap板子辣,不要被他的编号误导了,我们还是需要按照排名来分裂,从上到下标号1−n1-n1−n即可,注意在按照排名分裂的时 ...

  2. luogu P2596 [ZJOI2006]书架

    背景: 整整一天. 题目传送门: https://www.luogu.org/problemnew/show/P2596 题意: n n n本书, 5 5 5中操作. 1. 1. 1.把某本书放在最上 ...

  3. P2596 [ZJOI2006]书架(fhq treap)

    P2596 [ZJOI2006]书架 我们用fhq treap来完成这一题 对于一个新插入的节点我们取权值为其索引值,其所记录的valuevaluevalue是其当前索引所在位置. 操作一:把索引为v ...

  4. 浅尝无旋Treap (基于洛谷P3391 文艺平衡树)

    说是浅尝吧,确实也挺浅的,完全是基于下面这道题写的↓ 洛谷P3391 自己去看题,我是懒得粘了... 分析 其实也没有什么好分析的,这就是一道Splay树的模板题,解决一般的Treap不能解决的区间维 ...

  5. 无旋treap 文艺平衡树

    因为需要用到区间修改,所以该用splay(尚未填坑)或者无旋treap(刚刚填上) 最开始的建树用到了建笛卡尔树的方法,把id大于当前点的点不断出栈,又因为这道题的点是按序入栈的,所以当它无法让更多点 ...

  6. 无旋treap的简单思想以及模板

    因为学了treap,不想弃坑去学splay,终于理解了无旋treap... 好像普通treap没卵用...(再次大雾) 简单说一下思想免得以后忘记.普通treap因为带旋转操作似乎没卵用,而无旋tre ...

  7. 模板:无旋treap

    文章目录 前言 操作 合并 分裂 插入 删除 查找第k大 查询x的排名 前驱后继 完整代码 所谓无旋treap,就是不带旋转的treap 前言 现在"理论上"我会四种平衡树了 之前 ...

  8. 洛谷 P2596 [ZJOI2006]书架 解题报告

    P2596 [ZJOI2006]书架 题目描述 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书, ...

  9. BST、AVL、BTree、B+Tree、B*Tree、23Tree、234Tree、TTree、RBTree、LLRBTree、AATree、SplayTree、Treap、无旋Treap、scap

    喜欢这篇文章吗?喜欢的话去看博主的置顶博客,即可依据分类找到此文章的原版得到更好的体验, 图片及代码显示的问题,笔者深感抱歉,想要更好的体验去原博文即可. title: tree mathjax: t ...

最新文章

  1. 函数的四种调用模式.上下文调用.call.apply
  2. c语言字符串文库总结,C语言字符串.ppt
  3. android RxJava(RxAndroid)的简单使用
  4. mac 抓包工具chares破解
  5. sts 创建webservice项目_常用的RPC架构---WebService
  6. VMware vCenter 资源池
  7. 16F877A和24C02通信汇编语言,PIC16f877A读写24c02程序
  8. linux五周第三次课(3月7日)笔记
  9. linux c 读写 ini 配置文件
  10. xposed框架安装使用教程(第一篇)
  11. 三自由度机械手腕设计机构设计
  12. 整体改革理论(简介)
  13. CSS3动画animation认识和Animate.css的使用
  14. 三峡学院计算机调剂,重庆三峡学院2019考研调剂信息公告
  15. EXCEL 连接 ORACLE 查询数据到表格 中文乱码 中文变成?
  16. linux audacity,linux下编译安装音频处理audacity-2.0.3教程
  17. OIS利率查询_图表加数据OIS隔夜基准利率掉期
  18. 招聘运维开发leader
  19. 第四平方和定理,用c语言实现
  20. 想要了解大厂PM吗,来看看这几款软件

热门文章

  1. OpenCV4 C++学习 必备基础语法知识二
  2. Canny-VO: 基于几何3D-2D边缘对准的RGB-D视觉里程计
  3. 配置ssh公钥登录提示还是输入密码
  4. 2分钟读懂Hadoop和Spark的异同
  5. Flask rst 文档转换为html格式文件
  6. 寻找网页设计灵感的200佳网站推荐(系列二)
  7. CentOS Samba 服务器的构建(转)
  8. Vivado安装器件不全
  9. java实现stack search_Java Stack search()用法及代码示例
  10. python编程到底难不难_养成下面几个编程习惯,学习python并不难!