题目链接:点击查看

题目大意:给出一个序列,初始时是乱序的,要求根据规则排序,规则是,遍历每一个点 i ,使得区间 [ i , a[ i ] ] 内的数翻转,a[ i ] 表示的是数列中第 i 大的数,可以看出每次翻转都会使得第 i 大的数字到达第 i 个位置上去,题目要求输出每次第 i 大的数的位置

题目分析:一个题拖了好久。。说白了还是太懒了,一道比较经典的伸展树进阶题,做完之后还是收获了不少东西的,首先分析题意,因为每次翻转之后,第 i 个数在后续就没有任何作用了,所以每次我们可以直接在翻转后删掉第 i 个数,而且初始时给出的数值我们可以转换为相对大小,我们需要的只是其相对位置,所以可以排序,这样就可以获得相对位置了,也就是第 i 大的数的初始下标编号,从而在建树的时候,我们可以直接按照区间的下标建树,这样做的好处是,在splay中编号为 i 的结点对应的值就是 i ,方便后续的修改等操作,对于每一次操作,每次把第 i 个位置下标的结点旋转到根,我们就可以直接将结点 i 旋转到根,每次操作后splay的中序遍历就是当前维护的数列了(其实每个 a[ i ] 在这里是第 i 大的数的位置,可能有点绕,但尽量去理解吧),因为我们这个题目主要是围绕着相对大小展开的,所以答案也就取决于当前 a[ i ] 左边有多少个数字,也就是 i + sum[ ch[ root ][ 0 ] ] 了,注意这里是 a[ i ] 左边有多少个数字在 a[ i ] 的左边,而不是比 a[ i ] 小的有多少个数字,这里一定要理解,因为翻转之后区间的绝对大小关系是比较混乱的,但是相对大小关系是比较明确的,查询完后记得翻转+删除

到这里可能只是知道了如何解决这个题,下面的实现才是真正的难点,简单总结一下这个题目需要让我们完成的操作就是,循环 n 次,每次找到第 i 大的数,输出 i + sum[ ch[ root ][ 0 ] ] ,翻转根的左子树,删除根

首先找到第 i 大的数并不难,因为我们上述的相对关系的转换,第 i 大的数,其位置也就是我们排序后获得的 pos 了,这里如果不懂先看代码理解一下吧,总之就是可以直接获得 pos 的位置,不需要再用find或rank函数寻找,其次直接输出答案,而对于翻转而言,因为之前在洛谷做的那个文艺平衡树,是对于 m 次翻转后,一次更新得到答案,那个题对于pushdown的要求比较低,随便水水就过去了,但这个题不一样,这个题对于每一次操作,都必须要实时更新,不能拖到最后一次更新,这就意味着每次向上rotate或向下find时,都必须要时刻跟着pushdown,不然会因为没有及时更新导致答案错误,对于pushdown的位置,我暂且是先记住的:

  1. rotate里pushdown两次
  2. splay里pushdown三次
  3. find/rank里每一层都要pushdown

find/rank里的pushdown并不难想,因为向下走那么肯定得pushdown啊,但splay和rotate里的pushdown我还没琢磨清楚,先背过吧,而对于根的删除,也是有技巧的,我们分为两种情况:

  1. 根有左子树,此时我们需要找到一个节点来代替根才行,可以选择去右子树中,找到一个最大的值,也就是排rank1的一个节点,这个节点一定没有左儿子,直接将这个节点的左儿子与根节点的左儿子建立关系就好了
  2. 根没有左子树,这个简单,直接将根节点重置为根的右儿子就好了

当然,上述操作对于右子树同样成立,看个人喜好吧

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;class Splay//存储规则:小左大右,重复节点记录
{#define root e[0].ch[1]   //该树的根节点private:class node{public:int lazy;int v,father;//节点值,父级节点 int ch[2];//左孩子=0,右孩子=1int sum;//自己+自己下级有多少节点。在根节点为1。int recy;//记录自己被重复了几次};node e[N];//Splay树主体int n,points;//使用存储数,元素数void update(int x){e[x].sum=e[e[x].ch[0]].sum+e[e[x].ch[1]].sum+e[x].recy;}int identify(int x){return e[e[x].father].ch[0]==x?0:1;}void connect(int x,int f,int son)//连接函数。用法:connect(son,father,1/0){e[x].father=f;e[f].ch[son]=x;}//作用:使得x的father=f,f的son=x.void rotate(int x){int y=e[x].father;pushdown(y);pushdown(x);int mroot=e[y].father;int mrootson=identify(y);int yson=identify(x);int B=e[x].ch[yson^1];connect(B,y,yson);connect(y,x,(yson^1));connect(x,mroot,mrootson);update(y);update(x);}void splay(int at,int to)//将at位置的节点移动到to位置{pushdown(at);to=e[to].father;while(e[at].father!=to){int up=e[at].father;int ff=e[up].father;pushdown(ff);pushdown(up);pushdown(at);if(e[up].father==to) rotate(at);else if(identify(up)==identify(at)){rotate(up);rotate(at);}else{rotate(at);rotate(at);}}}int crepoint(int v,int father){n++;e[n].lazy=0;e[n].v=v;e[n].father=father;e[n].sum=e[n].recy=1;e[n].ch[0]=e[n].ch[1]=0;return n;}void destroy(int x)//pop后摧毁节点 {e[x].v=e[x].ch[0]=e[x].ch[1]=e[x].sum=e[x].father=e[x].recy=0;if(x==n) n--;//最大限度优化}public:void init(){points=n=root=0;e[root].v=e[root].father=e[root].sum=e[root].recy=e[root].ch[0]=e[root].ch[1]=0;}int getroot(){return root;}int find(int v)//用于外部的find调用{int now=root;while(true){if(e[now].v==v){splay(now,root);return now;}int next=v<e[now].v?0:1;if(!e[now].ch[next]) return 0;now=e[now].ch[next];}}int build(int v)//内部调用的插入函数,没有splay {points++;if(points==1)//特判无点状态 {root=n+1;crepoint(v,0);}else{int now=root;while(true)//向下找到一个空节点 {e[now].sum++;//自己的下级肯定增加了一个节点 if(v==e[now].v){e[now].recy++;return now;}int next=v<e[now].v?0:1;if(!e[now].ch[next]){crepoint(v,now);e[now].ch[next]=n;return n;}now=e[now].ch[next];}}return 0;}void push(int v)//插入元素时,先添加节点,再进行伸展 {int add=build(v);splay(add,root);}void pop(int v)//删除节点 {int deal=find(v);if(!deal) return;points--;if(e[deal].recy>1){e[deal].recy--;e[deal].sum--;return;}if(!e[deal].ch[0]){root=e[deal].ch[1];e[root].father=0;}else{int lef=e[deal].ch[0];while(e[lef].ch[1]) lef=e[lef].ch[1];splay(lef,e[deal].ch[0]);int rig=e[deal].ch[1];connect(rig,lef,1);connect(lef,0,1);update(lef);}destroy(deal);}int rank(int v)//获取值为v的元素在这棵树里是第几小 {int ans=0,now=root;while(true){if(e[now].v==v) {ans+=e[e[now].ch[0]].sum;splay(now,root);return ans+1;}if(now==0) return 0;if(v<e[now].v) now=e[now].ch[0];else{ans=ans+e[e[now].ch[0]].sum+e[now].recy;now=e[now].ch[1];}}return 0;}int atrank(int x,int rt)//获取第x小的元素的值 {if(x>points) return -inf;int now=rt;while(true){pushdown(now);int minused=e[now].sum-e[e[now].ch[1]].sum;if(x>e[e[now].ch[0]].sum&&x<=minused) break;if(x<minused) now=e[now].ch[0];else{x=x-minused;now=e[now].ch[1];}}splay(now,root);return e[now].v;}int upper(int v)//寻找该值对应的一个最近的上界值 {int now=root;int result=inf;while(now){if(e[now].v>=v&&e[now].v<result) result=e[now].v;if(v<e[now].v) now=e[now].ch[0];else now=e[now].ch[1];}return result;}int lower(int v)//寻找该值对应的一个最近的下界值 {int now=root;int result=-inf;while(now){if(e[now].v<=v&&e[now].v>result) result=e[now].v;if(v>e[now].v) now=e[now].ch[1];else now=e[now].ch[0];}return result;}void reverse(int pos,int i){splay(pos,root);//把第pos小的点旋到根e[e[root].ch[0]].lazy^=1;printf("%d ",i+e[e[root].ch[0]].sum);remove();}void pushdown(int k){if(k&&e[k].lazy){swap(e[k].ch[0],e[k].ch[1]);e[e[k].ch[0]].lazy^=1;e[e[k].ch[1]].lazy^=1;e[k].lazy=0;}}void remove(){int t=root;if(e[root].ch[1]){atrank(1,e[root].ch[1]);e[root].ch[0]=e[t].ch[0];if(e[t].ch[0])    e[e[t].ch[0]].father=root;}elseroot=e[root].ch[0];e[root].father=0;update(root);}#undef root
}tree;struct Node
{int pos,val;bool operator<(const Node& a)const{if(val!=a.val)return val<a.val;return pos<a.pos;}
}a[N];int main()
{
#ifndef ONLINE_JUDGE
//    freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
#endif
//    ios::sync_with_stdio(false);int n;while(scanf("%d",&n)!=EOF&&n){tree.init();for(int i=1;i<=n;i++){scanf("%d",&a[i].val);tree.push(i);a[i].pos=i;}sort(a+1,a+1+n);for(int i=1;i<n;i++){int pos=a[i].pos;tree.reverse(pos,i);}printf("%d\n",n);}return 0;
}

HDU - 1890 Robotic Sort(Splay-区间翻转+删除根节点)相关推荐

  1. xml文件中删除根节点

    //删除根节点 TiXmlDocument *myDocument = new TiXmlDocument(filefullPath.c_str()); myDocument->LoadFile ...

  2. bzoj 1552: [Cerc2007]robotic sort bzoj 3506: [Cqoi2014]排序机械臂(splay区间翻转)

    1552: [Cerc2007]robotic sort Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 1206  Solved: 460 [Submi ...

  3. Luogu P3165 Splay区间翻转

    脑残记录: 这周末2天只做了一道平衡树题目(2天被一道题限住),发现自己的很多问题, 学了算法及其性质 但是总是做不出来题目,很难利用学过的性质, 同样这种情况再次发生在今天 没有运用的能力,思维太差 ...

  4. splay区间翻转(bzoj 3223: Tyvj 1729 文艺平衡树)

    3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 4854  Solved: 2844 [Submit][S ...

  5. P3391 文艺平衡树(Splay区间翻转)

    题解: Splay区间反转的模板题. 翻转[3,5]时,我们可以将3的前驱找到,5的后继找到,然后根节点的右儿子的左子树就是属于[3,5]这个区间的数,可以自己画画就容易知道了. 然后就是给这个结点打 ...

  6. HDU - 3911 Black And White 区间翻转+区间连续最长

    区间翻转的裸题,wrong了两发,没找bug,直接又打了一遍,这么大量的代码找bug是疯了. 重新开始做线段树的一些题了,感觉对于pushdown与lazy数组有点印象了. 首先,lazy数组是记录的 ...

  7. avl删除根节点图解_图解 6 种树,你心中有数吗。。。

    数据结构这门课程是计算机相关专业的基础课,数据结构指的是数据在计算机中的存储.组织方式. 我们在学习数据结构时候,会遇到各种各样的基础数据结构,比如堆栈.队列.数组.链表.树...这些基本的数据结构类 ...

  8. 【HDOJ】1890 Robotic Sort

    伸展树伤不起啊,很容易wa,很容易T,很容易M. 1 /* 1890 */ 2 #include <iostream> 3 #include <string> 4 #inclu ...

  9. Splay ---- 区间翻转 区间最大值 区间加 P4146 序列终结者

    题目链接 题目大意: 解题思路: 这是一道很入门的Splay的题目 但是第一次写有很多坑点 首先是maxmaxmax值的更新:就是因为这个点的最小值是会出现负数负数的,当你左右儿子有一个没有的话,那么 ...

最新文章

  1. 【学习笔记】10、循环语句—for
  2. SAP CRM interaction center呼叫中心的一些性能问题的分析
  3. 享元模式-Flyweight(Java实现)
  4. Java EE 7 / JAX-RS 2.0 – REST上的CORS
  5. 未来我们需要一辆什么样的智能汽车?
  6. CVPR2017-图像特征匹配-GMS:基于网格的运动统计的快速且极度鲁棒的图像特征匹配算法
  7. 视频教程-三课时精通matlab拉普拉斯变换和逆变换-Matlab
  8. 一串代码远程控制电脑关机
  9. 八皇后问题(回溯算法)
  10. IOI2008 island
  11. 第三章(第一部分) 月夜猫の魅 友谊的决裂
  12. 华为MateBook电脑连接蓝牙鼠标使用失灵解决方法
  13. 仙侣奇缘2 无法 启动mysql_仙侣奇缘2服务端
  14. 开发者应用盈利最佳渠道-KeyMob移动广告聚合平台
  15. CCF 201903-5 317号子任务(60分)
  16. 【计算机网络】实验3:虚拟机配置测试实验
  17. Semi-Supervised Semantic Segmentation with Cross-Consistency Training论文笔记
  18. JS 面试问题: 手写 new
  19. 北京大学,南下布局!
  20. AcWing1884. COW

热门文章

  1. 计算机图形与游戏技术,宾夕法尼亚大学计算机图形与游戏技术研究生Offer及录取要求...
  2. 国立大学 计算机,新加坡国立大学 计算机
  3. MySQL安全等于的介绍
  4. MySQL连接查询的分类
  5. MySQL 高级 游标介绍
  6. Spring用户自定义类型
  7. Survivor区详解
  8. MyBatis关键配置
  9. MyBatis 缓存详解-开启二级缓存的方法
  10. web开发中常用的概念