CODEVS-3303-翻转区间

描述

http://codevs.cn/problem/3303/


分析

本题就是一个普通的通过打标记实现区间翻转的splay题目. 之前发过翻转卡片和这个题很像. 这里主要是想仔细分析一下标记的实现以及一些问题. 为后面的维护数列做一些准备工作.


  • 首先明确, 为什么可以通过标记的方式记录翻转信息.

    splay 的一棵子树代表的就是一个连续的区间, 因为splay是BST, 满足结点o的左儿子(lc) <= 根节点(o) <= 右儿子(rc). 并且如果o是其父结点的左儿子, rc一定比o的父节点要小; 如果o是其父结点的右儿子, lc一定又比o的父节点要大. 这样就保证了区间是连续的, 而且这个区间的序列就是这棵子树的先序遍历的结果.


  • 既然子树是一个封闭的区间了, 对其打上翻转标记后就可以表示区间已经被翻转了. 那么如何标记下传呢? 首先根结点的左右子树应该交换一下, 也就是以根结点o为分界点, 比o小的lc交换到比o大的rc上, 比o大的rc交换到比o小的lc上. 接着以左右子结点为根节点分别将标记下传到左右两棵子树上… 层层下传, 最终实现整个区间的翻转. 这就是分治的思想. 当然实现中需要时才将标记下传一步.

  • 最后一个问题, 何时进行标记下传? 标记下传后如何维护其父结点以及祖先的值?

    这里还要分两种情况, 第一种是自底向上的splay, 这种需要在find()时把遇到的所有结点进行标记下传,find()会经过到达目标结点的所有结点. 另一种是自顶向下的splay, 需要在splay操作时把根结点和目标所在的子结点标记下传. 因为既要找到目标结点在根结点的方向(左子树还是右子树), 又要找到目标结点在子结点的方向.

    而标记下传后需不需要维护祖先结点的值呢? 其实不需要过多维护, 因为splay操作自带维护操作——旋转操作里就有.


代码

https://code.csdn.net/snippets/608231

时间: 2024-10-06 15:11:22

CODEVS-3303-翻转区间的相关文章

codevs 2946 翻转游戏

2946 翻转游戏 题目描述 Description 现有n个数字a1,a2...an,其值均为0或1. 要求对着n个数中连续的若干个数进行一次取反(0->1,1->0),求所能得到的最多的1的数目. 输入描述 Input Description 第一行,n 第二行,n个数 输出描述 Output Description 能得到的最多的1的数目 样例输入 Sample Input 41 0 0 1 样例输出 Sample Output 4 数据范围及提示 Data Size & Hin

HDU3487(splay区间翻转+区间切割)

题意:开始有一个1,2,3,...n的序列,进行m次操作,cut a b c将区间[a,b]取出得到新序列,将区间插入到新序列第c个元素之后.filp a b 将区间a,b翻转,输出最终的序列. 思路:对于cut操作我们需要先提取出区间[a,b]然后,先暂时分裂出去,然后以c为边界分裂左右两部分,然后合并左边的和区间[a,b],将最大的旋转至根,然后和右边的合并.对于filp操作,我们可以使用lazy标记,先不翻转,需要的时候再翻转,代码如下 [cpp] view plaincopy /****

CODEVS 1082 (区间查询+区间求和)

题目链接:http://codevs.cn/problem/1082/ 注意更新区间求和时,应该加上的是这一段区间包含的元素的个数乘以更新的值 #include <cstdio> #include <iostream> #include <algorithm> #include <cmath> #include <queue> #include <vector> #include <cstring> #include &l

codevs 3303

还没写过splay树上的lazy标记.写得还算顺利,不过自己出数据错了浪费了好多时间QAQ 在debug过程中深刻体会到了gdb的好处orz 1 #include<bits/stdc++.h> 2 #define inc(i,l,r) for(i=l;i<=r;i++) 3 #define dec(i,l,r) for(i=l;i>=r;i--) 4 #define mem(a) memset(a,0,sizeof(a)) 5 #define NM 100000+10 6 #def

HDU 4453 (splay 插入删除翻转区间加单点查)

//白色上的模板,先静态申请结构体数组,再动态使用,时间应该更快:还有个小技巧,它的空指针用真实的null指针代替,这样即使访问了null的内容也没关系,减少出错的可能性 #include<cstdio> #include<algorithm> #include<vector> using namespace std; struct Node { Node *ch[2]; int s; int flip; int v; int add; int cmp(int k) c

【NOIP模拟_54测试】【并查集】【二进制】【搜索】【区间序列类】

第一题 Mushroom的序列 大意: 给一个序列,求一段连续最长区间满足:最多改变一个数,使得区间是严格的上升子序列. 解: 直接扫描一遍,记一个最长上升子序列编号.然后从每一个编号为1 的点来判断是否可以将两个序列合并,有两种情况,讨论需要注意多种子情况...我可能想的比较复杂,所以第一遍写的时候少考虑了一些相等的情况,就WA 了一个点. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #i

Salty Fish(区间和)

Problem 2253 Salty Fish Accept: 35    Submit: 121Time Limit: 1000 mSec    Memory Limit : 32768 KB Problem Description 海边躺着一排咸鱼,一些有梦想的咸鱼成功翻身(然而没有什么卵用),一些则是继续当咸鱼.一个善良的渔夫想要帮这些咸鱼翻身,但是渔夫比较懒,所以只会从某只咸鱼开始,往一个方向,一只只咸鱼翻过去,翻转若干只后就转身离去,深藏功与名.更准确地说,渔夫会选择一个区间[L,R]

Splay 区间反转

同样的,我们以一道题来引入. 传送门 这次的任务比较少,只要求进行区间反转.区间反转? 这个好像用啥都是O(n)的吧--(这次vector,set也救不了你了) 我们来使用splay解决这个问题.我们既然要反转一段区间,那我们肯定要把这个区间弄到一个地方.我们想一下上次所讲的删除操作,我们把要删除的数的前驱后继都找了出来并且一个旋转到根,一个到根的右儿子.我们思考一下发现,如果把这个区间第一个数的前一个数(l-1)旋转到根,把区间最后一个数的后一个数(r+1)旋转到根的右儿子,那么现在根的右儿子

【集训第三天&#183;疯狂训练】哦,顺带学习了manacher

虽然说是疯狂训练吧,但是也没写多少题,就把伸展树的操作熟悉了一下,ac了5个题目. 一整天没啥可吐槽的,除了昨天在机房打游戏的某位朋友翻车后和教练谈了谈心2333 说题吧.. 1.BZOJ1208 HNOI2004 宠物收养所 这个题思路很简单,当做模板题打,在模板题中也算是简单的了,涉及操作:前驱,后继,插入,删除..输入进来就插入,领养走就删除,并没有什么可说的.加上一个标记表示现在树上表示的是宠物还是人. 另外听说可以用set做,但是我并不会set(???set都不会吃屎吧). CODE:

Splay复习

CODEVS 1743 翻转卡片 小A将N张卡片整齐地排成一排,其中每张卡片上写了1~N的一个整数,每张卡片上的数各不相同. 比如下图是N=5的一种情况:3 4 2 1 5 接下来你需要按小A的要求反转卡片,使得左数第一张卡片上的数字是1.操作方法:令左数第一张卡片上的数是K,如果K=1则停止操作,否则将左数第1~K张卡片反转. 第一次(K=3)反转后得到:2 4 3 1 5 第二次(K=2)反转后得到:4 2 3 1 5 第三次(K=4)反转后得到:1 3 2 4 5 可见反转3次后,左数第一