splay经典题目举例

数据结构专攻告一段落吧,把这段时间和以前做过的splay的题目拿出来晒晒,每题都写了一下简单的解题报告,用白色的字体,每道题的做法最好要多花时间琢磨琢磨,splay的题目主要就是中间过程的处理上比较麻烦。为了方便,我对区间操作都是将l-1结点splay到根,r+1结点splay到根的右子树,定义根的右子树的左子树为关键结点。


常见的splay问题的处理

首先是标记,下放标记和上传标记最好分开写,否则的话非常容易出问题,我们下放标记记录的是孩子结点的信息,所以我们如果要对一个结点打标记,我们要先处理好这个结点,比如说这个结点权值加上一个值,左右子树交换等等。

其次是区间操作上的方便性问题,对于平常的区间操作,如果每次询问都是删除[l,r]区间,这样我们可以在序列左右两端加上两个哨兵,这样处理起来方便,但是有的题目这样反而不好处理,比如翻转下标小于x的所有数,这样左边界不能成为翻转的对象。

最后是比较重要的一点,就是如果减少时间的开销,一般我们对splay做insert操作的时候,我们要将加入到结点,或者子树的根结点splay到根,这样我们可以防止splay出现退化的情况,虽然这样还是会退化,但是均摊复杂度是可以接受的。


HDU3487  Play with Chain 

http://acm.hdu.edu.cn/showproblem.php?pid=3487

给你一个序列,有两个操作,一个是把一段区间翻转,另外一个是把一段区间截下来放到另外一个位置,问你最后的序列长什么样。


对于FLIP操作直接将l-1和r+1位置的结点伸展,然后在关键结点出将标记处理了。

对于CUT操作,首先我们将关键子树分离,记得分离之前要pushdown关键根,然后将关键子树接到新的子树上去,注意这时候我们要记得将关键子树的父亲结点设为关键根,然后对关键根进行update操作。


NOI2005 维护数列

这题巴蜀的OJ挂掉了,现在没办法交,不过题目还是很有意思的,给你一个数列问你一系列的关于数列的问题,除了区间翻转以外的问题都可以转化为线段树来做,我们可以先将线段树想清楚,然后转化成splay的模型就可以了。


利用splay来维护整个序列,最大子字段和注意一下需要处理的是必须至少选一个,所以我们加入的哨兵还有空结点的所有特征值为负无穷大,例外的是空结点的size域以及和值应为0。

至于平衡树的处理,标记都是下放,特征值一定都是向上传递的,写的时候不要写错,此外要注意这题懒操作要写好,否则会TLE。

当时WA了一天,主要错的地方在于没有处理好哨兵的特征值,此外一定要注意我们可以把哨兵统一到结点中,只要其特征值取的好。

本题注意全部数字都是负数的情况。


POJ3580  Super Memo

http://poj.org/problem?id=3580

这题也是比较经典的入门题了,模型很裸,但是写起来要纠结一点儿。


这题和常规的splay题类似,唯一的区别就是那个区间旋转,我们可以找到分界点,然后左半边右半边进行一次reverse,然后整个区间再进行一次reverse。


NOI2003 文本编辑器

很多OI选手津津乐道的题目,这题可以算是相当恶心的题目了,给你一个文本编辑器的模型,叫你模拟全部的操作,直接用splay来维护,然后加一个指针,表示现在光标的位置。


标准的splay题,注意这题delete和get可能会越界,要判断,旋根的时候右边界必须是size-1。

此外数组要开足够大,开1024^2*2差不多。注意中间的处理不要写的太复杂,否则写挂了也不好处理。


HDU1890 Robotic Sort

http://acm.hdu.edu.cn/showproblem.php?pid=1890

这题是给你一些不同的数,每次选择最小的翻转回它应该在的位置,叫你模拟这个过程。这题最后要保证相同数字的相对位置不变。


经典splay问题,我们可以考虑记录每一个高度对应的结点的指针,用一个数组存起来,然后每次将其旋转到根,根的左子树加上i加上1当做答案,然后翻转左子树,删除根结点。

注意为了操作时产生不必要的麻烦,建议不加哨兵,删除根的时候特判一下,如果有左右孩子,那么就去找前驱后继,删除,否则直接root替换即可。


FZU1978 Repair the brackets

http://acm.fzu.edu.cn/problem.php?pid=1978

给你一个括号序列,动态更新区间,包括区间取反,区间翻转,区间赋值,查询使得一个区间变成规则括号串的最小花费,对于一个位置把括号反向,需要1的花费。


利用splay来维护全部的操作,定义左括号是1右括号是-1,对于一个Query,我们计算区间从左到右的和值最小值,记录a为这个最小值的绝对值,b为这个区间的和减去这个最小值,那么答案就是a/2+b/2,注意都是上取整。

由于需要进行区间的翻转,所以要记录右边的值,此外由于有区间的取反,所以要记录最大值。

最后需要注意的是最小值不超过0,最大值不小于0。


HDU2475 Box

http://acm.hdu.edu.cn/showproblem.php?pid=2475

给你一些盒子的相对关系,动态的对盒子进行合并、拆分,问你某个盒子最外层的盒子是什么。这题是经典的括号序列问题,如果不懂的,可以去百度一下,应该有相关的资料~


利用splay来维护括号序列,对于一个DFS序列,我们入栈记录一次,出栈记录一次,我们用x表示x这个结点入栈的点,用x+n表示x出栈的点,那么对于这个问题可以轻松转化过来。

对于MOVE操作,如果可以移动,我们进行cut操作和add操作。

对于cut操作,我们将x和x+n分别旋根,这样我们可以得到一个x和x+n,x+n是根,x是其左子树的某一个结点,这样x的左子树和x+n的右子树是无效的,我们剪掉它们,并且将x+n的右子树接到x的左子树最后,接着把x+n旋根,保证平衡性。

对于add操作,我们将新的父亲结点旋到根,x+n旋到根,然后进行连接,注意新的父亲结点的右子树,需要接到x+n的右子树上去。

对于初始化,我们可以确定每一个结点的父亲结点,然后逐个进行add就是。

最后的问题就是如何判断y是不是x的子树,这个的判断方法是先对x执行cut操作,注意这时候两个子树不要合并,然后再去查询y的根结点,如果y的根结点是x,说明y是x的子树,最后记得复原。复原的时候要把x和x+n旋根,两个子树旋根,然后拼接。

查询一个结点的根结点,其实就是DFS序列的起点,也就是所在树的最小元素,将x旋根,找左子树的最小值即可。


HDU3726 Graph and Queries

http://acm.hdu.edu.cn/showproblem.php?pid=3726

给你一个图,有一些无向边,现在有三种操作,一个是改变点权,一个是删掉一条边,一个是查询结点x所在连通块第k大的点权。


我们离线从后向前处理,对于每一个删边操作相当于是一个合并操作,然后对于每一个点来说,我们拉一个邻接表表示修改的权值。那么,对于每一个修改操作我们就将节点splay到根,然后删掉根结点,修改根结点,然后把结点加入树中。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值