splay

P3369 【模板】普通平衡树

筅森魡賤 提交于 2020-01-26 17:59:22
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入 x x 数 删除 x x 数(若有多个相同的数,因只删除一个) 查询 x x 数的排名(排名定义为比当前数小的数的个数 +1 + 1 ) 查询排名为 x x 的数 求 x x 的前驱(前驱定义为小于 x x,且最大的数) 求 x x 的后继(后继定义为大于 x x,且最小的数) 很好的模板题 借鉴了一位在役国集选手 #include<bits/stdc++.h> #define MAXN 300005 using namespace std; struct Splay{ int num[MAXN],ch[MAXN][2],sz[MAXN],f[MAXN],cnt[MAXN],rt,tot; int get(int x){ return (x==ch[f[x]][1]); } int up(int x){ sz[x] = cnt[x]; if(ch[x][0])sz[x] = sz[x]+sz[ch[x][0]]; if(ch[x][1])sz[x] = sz[x]+sz[ch[x][1]]; return 0; } int rote(int x){ int y = f[x],z = f[y],k = get(x),p = get(y); if(y==0)return 0; if(z)ch[z][p]

Splay

放肆的年华 提交于 2020-01-22 20:24:03
二叉查找树,对于任意一个节点,该节点的关键码大于它的左子树中任意节点的关键码,该节点的关键码小于它的右子树中任意节点的关键码,且没有键值相等的点 二叉查找树的中序遍历是一个关键码单调递增的节点序列 数组及变量 \(fa[i]:\) 节点 \(i\) 的父节点 \(son[i][0]:\) 节点 \(i\) 的左儿子 \(son[i][1]:\) 节点 \(i\) 的右儿子 \(key[i]:\) 节点 \(i\) 的关键字 \(siz[i]:\) 以节点 \(i\) 为根的子树元素个数 \(cnt[i]:\) 节点 \(i\) 所表示的元素的出现次数 \(tot:\) 共有多少元素 \(root:\) 树的根 函数 \(check:\) 判断节点 \(x\) 是它父亲的左儿子还是右儿子 \(pushup:\) 更新节点 \(x\) 的 \(siz\) \(rotate:\) 将是左儿子的右旋,是右儿子的左旋 \(splay :\) 进行伸展,不断 \(rotate\) 直到达到目标状态 \(insert:\) 插入一个值 \(find:\) 查找 \(x\) 的位置,并将其旋转到根节点 \(rnk:\) 查询 \(x\) 的排名 \(val:\) 查询排名为 \(x\) 的数 \(get:\) \(k=0\) 时,求 \(x\) 的前驱, \(k=1\) 时,求 \(x\) 的后继

LCT感悟

我是研究僧i 提交于 2020-01-22 10:25:16
动态树,可删边加边,树剖功能真包含于LCT Splay操作无论怎么旋,始终保持key的中序遍历,lct就是将深度当作key,用splay来维护树的形态 splay的翻转操作:将左子树和右子树交换,并打上标记。交换两子树意味着所有比它大的数都比它小,小者同理。本应所有节点执行此操作,因此打上标记。 lct对于答案的维护即是利用了splay的区间信息维护能力(splay首先维护的是原树上的链,其次key代表深度,所以点值来源: makeroot(x)旋上一点x,即指定x;access(y)在指定x的基础上打通x-y的路径并生成一颗平衡树;splay(y)旋上y,此时利用 平衡树自带的update,上旋的同时更新所有点值,使得点值等于子树所有点值的总信息(在模板题中是疑惑和),即可得到答案。 5 .关于cut的三个条件判断,是因为不仅y要在x的splay子树中,还要中序遍历中不存在其他中间点(中序遍历无中间点表示深度相差1,才表示有相邻边) 来源: CSDN 作者: xagadrd 链接: https://blog.csdn.net/qq_42725189/article/details/104067437

序列终结者

耗尽温柔 提交于 2020-01-22 05:54:00
一、题目 点此看题 二、解法 我学懂了!!!! 这道题可以写无旋 treap \text{treap} treap ,最近学了 splay \text{splay} splay ,我就讲一下它的操作吧。 这道题我们需要维护权值和最大值,那么我们如何查区间呢?我们先多建两个哨兵节点(一共 n + 2 n+2 n + 2 个点),然后我们把排名为 l l l 的点 A A A 转到根去,再把排名为 r + 2 r+2 r + 2 的点 B B B 转到 A A A 的右儿子(这里的排名要考虑哨兵),如果我们把 [ l , r ] [l,r] [ l , r ] 的区间看成一个点的话,那么这个点是 B B B 的左儿子,然后我们直接对 B B B 的左儿子去打标记,或者是询问。 我们怎么维护标记呢?在旋转的时候我们需要下传标记,在查排名的时候也要下传标记,然后就完了 q w q qwq q w q 。 # include <cstdio> # include <iostream> using namespace std ; # define inf 0x3f3f3f3f const int M = 200005 ; int read ( ) { int x = 0 , flag = 1 ; char c ; while ( ( c = getchar ( ) ) < '0' || c >

Spoj 10628. Count on a tree。

China☆狼群 提交于 2020-01-21 08:48:58
第一题没做出来不应该; 第二题不难想,就是写起来很麻烦; 第三题因为学了挺久的splay就直接写的splay,没太在意常数问题,一般情况下,第k值问题主席树是比splay稍快的; 盘子序列 【题目描述】 有 n 个盘子。盘子被生产出来后,被按照某种顺序摞在一起。初始盘堆中如果一 个盘子比所有它上面的盘子都大,那么它是安全的,否则它是危险的。称初始盘堆为 A,另外有一个开始为空的盘堆 B。为了掩盖失误,生产商会对盘子序列做一些“处 理”,每次进行以下操作中的一个:(1)将 A 最上面的盘子放到 B 最上面;(2)将 B 最上 面的盘子给你。在得到所有n个盘子之后,你需要判断初始盘堆里是否有危险的盘子。 【输入格式】 输入文件包含多组数据(不超过 10 组) 每组数据的第一行为一个整数 n 接下来 n 个整数,第 i 个整数表示你收到的第 i 个盘子的大小 【输出格式】 对于每组数据,如果存在危险的盘子,输出”J”,否则输出”Y” 【样例输入】 3 2 1 3 3 3 1 2 【样例输出】 Y J 【数据范围】 20%的数据保证 n<=8 80%的数据保证 n<=1,000 100%的数据保证 1<=n<=100,000,0<盘子大小<1,000,000,000 且互不相等 思路 倒序栈模拟; 代码实现 1 #include<cstdio> 2 #include<cstring> 3

《数据结构》学习笔记 第8章 高级搜索树 (Splay,RB,B-Tree)

◇◆丶佛笑我妖孽 提交于 2020-01-17 23:55:37
1, Splay Tree Splay Tree 定义:在一颗BBST中,某节点被访问,则随后将其移送至根节点。 数据局部性 逐层伸展 vs.双层伸展 精髓在于双层伸展(可减弱最坏情况的影响) 算法实现: 重点包含了Splay,search,insert,remove四种操作。 Splay算法 四种情况,使用3+4统一算法; Search算法:不再属于静态操作,调用了Splay算法。 返回命中节点,或者(未命中)邻近节点。 Insert算法 Remove算法 综合评价: 典型应用:电脑操作系统。 2,B-Tree 3, Red-Black Tree 来源: https://www.cnblogs.com/sanlangHit/p/12207802.html

luoguP4647 [IOI2007] sails 船帆

痞子三分冷 提交于 2020-01-16 18:57:18
https://www.luogu.org/problemnew/show/P4647 首先发现答案与顺序无关,令 $ x_i $ 表示高度为 $ i $ 的那一行帆的个数,第 $ i $ 行对答案的贡献为 $ \frac{x_i * (x_i - 1)}{2} $ 先把旗杆按照高度从小到大排序,有一个显然的贪心是每次选择能放的地方帆最少的一行放一个帆,最少的一行放一个帆对答案的贡献一定最小,而后面的旗杆高度更高,能选择放帆的地方更多,这样可以保证答案最小(可以感性理解一下 那么我们的做法就出来了,对于旗杆 $ i $ 选择当前能放帆的最少的 $ k_i $ 行,放上帆,这个操作用平衡树实现,将原序列前 $ k $ 小拿出,加上 $ 1 $ 后放回去,这个时候可能会使平衡树的性质被打破,如 $ 0 $ $ 0 $ 将第一个数加 $ 1 $ 再放回去就变成了 $ 1 $ $ 0 $,所以对于分裂出来的前 $ k $ 小中的最大值还要再次分裂,如 $ 0 $ $ 1 $ $ 1 $ $ 1 $ $ 2 $ 前 $ 3 $ 小要加 $ 1 $,先分裂成 $ 0 $ $ 1 $ $ 1 $ 和 $ 1 $ $ 2 $,然后分裂成 $ 0 $ ,$ 1 $ $ 1 $ 和 $ 1 $ $ 2 $,区间加后放回原序列,先变成 $ 1 $ {这里放原来的 $ 1 $ $ 1 $ } $ 2 $

平衡树(splay)学习笔记

送分小仙女□ 提交于 2020-01-15 13:12:30
Splay和一般的BST的区别大概就是:每次插入一个元素后,把它旋转到根 其核心操作就是$splay$和$rotate$ Rotate:把$x$转到$x$的父亲的位置上   $rotate$很好理解,自己画个图玩玩就会了, 代码要注意细节 void pushup(int x) { sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x]; } void rotate(int x) { int y=fa[x],z=fa[y],k=(x == ch[y][1]); if(z) ch[z][y == ch[z][1]]=x; fa[x]=z; ch[y][k]=ch[x][k^1], fa[ch[x][k^1]]=y; ch[x][k^1]=y, fa[y]=x; pushup(y); pushup(x); } Splay:把$x$转到目标$goal$的儿子的位置上(若$goal=0$,则把$x$转到根节点)   分三种情况:     1.$x$与$x$的父亲所属的儿子的种类相同(例如$x$是$y=fa[x]$的左儿子,$fa[x]$是$z=fa[fa[x]]$的左儿子)     2.与上面一种相反     3.$fa[fa[x]]=goal$   对于第一种情况,先把$y$转到$z$,再把$x$转到$y$   对于第二种情况,把$x$一路转上去   对于第三种情况

bzoj1805: [Ioi2007]Sail 船帆

浪子不回头ぞ 提交于 2020-01-15 07:47:48
可以发现旗杆的顺序是没有用的,对于每列,它的答案是它的最大值mx*(mx+1)/2 高度由小到大排序旗杆,问题可以转化为在前h行选k个最小的值 考虑激情splay乱搞(我只会splay......) 设树中序遍历第i个点的d值表示当前最后一个旗帜上面的数字为i-1的列的数量 我们可以二分一下求出我们要利用到第几个点x,对于x之前的点,他们的d值都要全部送给后一个点 所以我们可以删掉x这个点,并在最前面加一个点,这就相当于整体向右移动了一位 对于x这个点单独处理,删除前计算出留在x的数目和给x+1的数目,处理完以后再单独添加 #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; typedef long long LL; struct trnode { int f,son[2],c,d,s; }tr[410000];int trlen,root,cnt; void update(int x) { int lc=tr[x].son[0],rc=tr[x].son[1]; tr[x].c=tr[lc].c+tr[rc].c+1; tr[x].s=tr[lc].s+tr[rc].s+tr

[NOI2004]郁闷的出纳员

无人久伴 提交于 2020-01-13 18:52:35
题目描述 OIER 公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。 工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。 老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。 好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧? 如果某个员工的初始工资低于最低工资标准,那么将不计入最后的答案内。 输入格式 第一行有两个非负整数 \(n\) 和 \(\min\) 。 \(n\) 表示下面有多少条命令, \(\min\) 表示工资下界。 接下来的 \(n\) 行,每行表示一条命令