[BZOJ 1552] 排序机械臂

Splay大法是坠吼滴!

1552: [Cerc2007]robotic sort

Time Limit: 5 Sec  Memory Limit: 64 MB
Submit: 436  Solved: 186
[Submit][Status][Discuss]

Description

Input

输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000。第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号。

Output

输出共一行,N个用空格隔开的正整数P1,P2,P3…Pn,(1 < = Pi < = N),Pi表示第i次操作前第i小的物品所在的位置。 注意:如果第i次操作前,第i小的物品己经在正确的位置Pi上,我们将区间[Pi,Pi]反转(单个物品)。

Sample Input

6
3 4 5 1 6 2

Sample Output

4 6 4 5 6 6

HINT

Source

HNOI2009集训Day6

还算水的一道区间维护题目, 操作涉及区间翻转所以要用无旋 $Treap$ 或者$Splay$ .

主要思路是每次先建出这棵平衡树, 维护以结点为根的子树的大小, 最小值和最小值所在的子树(左/右/根), 然后每次根据保存的最小值的位置去查找并根据子树大小计算它所在的下标. 然后每次找到之后将其作为区间右端点, 左端点由于是顺序的所以直接从 $1$ 到 $n$ 枚举, 将这个选定的区间翻转即可. 翻转后这个最小值会对后面的计算产生额外影响所以要将已经排好序的结点的值重置为 $\infty$ 并向上更新.

以及题目要求该排序必须做到稳定, 所以我们保存数据时可以使用 $std::pair$, 第一关键字为键值, 第二关键字为该键值初始时的下标.

虚拟结点为了防止干扰答案 (本题中求最小值是针对整棵树, 而不像某维修数列全是指定区间结果虚拟结点一直在待求值的子树外) 键值与最小值均为 $\infty$,

然后就是注意没事多写几个标记下传操作, 标记下传多了顶多让你常数大点, 但下传少了可是会出人命的...

还有Splay的时候判根要判 $prt$ 是否等于传入的第二个参数而不是直接和 $NULL$ 比较(被坑), Splay内部的旋转要么以 $prt$ 为转轴要么以 $prt$ 的 $prt$ 为转轴, 不要脑抽写成以本结点为转轴 (又被坑了我好菜啊)

参考代码如下:

GitHub

  1 #include <vector>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <cstdlib>
  5 #include <iostream>
  6 #include <algorithm>
  7
  8 #define lch chd[0]
  9 #define rch chd[1]
 10 #define kch chd[k]
 11 #define xch chd[k^1]
 12
 13 const int INF=0x7FFFFFFF;
 14 typedef std::pair<int,int> pr;
 15
 16 class SplayTree{
 17 private:
 18     struct Node{
 19         pr k;
 20         int s;
 21         pr m;
 22         int mp;
 23         bool rev;
 24         Node* prt;
 25         Node* chd[2];
 26         Node(const pr& key){
 27             this->s=1;
 28             this->mp=-1;
 29             this->k=key;
 30             this->m=key;
 31             this->prt=NULL;
 32             this->lch=NULL;
 33             this->rch=NULL;
 34             this->rev=false;
 35         }
 36         ~Node(){
 37             delete this->lch;
 38             delete this->rch;
 39         }
 40         inline void Flip(){
 41             if(this==NULL)
 42                 return;
 43             this->rev=!this->rev;
 44             std::swap(this->lch,this->rch);
 45             if(this->mp!=-1)
 46                 this->mp^=1;
 47         }
 48         inline void PushDown(){
 49             if(this!=NULL&&this->rev){
 50                 this->lch->Flip();
 51                 this->rch->Flip();
 52                 this->rev=false;
 53             }
 54         }
 55         inline void Maintain(){
 56             if(this!=NULL){
 57                 this->s=this->lch->size()+this->rch->size()+1;
 58                 this->m=this->k;
 59                 this->mp=-1;
 60                 if(this->lch->min()<this->m){
 61                     this->m=this->lch->min();
 62                     this->mp=0;
 63                 }
 64                 if(this->rch->min()<this->m){
 65                     this->m=this->rch->min();
 66                     this->mp=1;
 67                 }
 68             }
 69         }
 70         inline pr min(){
 71             return this==NULL?std::make_pair(INF,INF):this->m;
 72         }
 73         inline int minp(){
 74             return this==NULL?-1:this->mp;
 75         }
 76         inline int size(){
 77             return this==NULL?0:this->s;
 78         }
 79         inline pr key(){
 80             return this==NULL?std::make_pair(INF,INF):this->k;
 81         }
 82     }*root;
 83     void Rotate(Node* root,int k){
 84         Node* tmp=root->xch;
 85         root->PushDown();
 86         tmp->PushDown();
 87         tmp->prt=root->prt;
 88         if(root->prt==NULL)
 89             this->root=tmp;
 90         else if(root->prt->lch==root)
 91             root->prt->lch=tmp;
 92         else
 93             root->prt->rch=tmp;
 94         root->xch=tmp->kch;
 95         if(root->xch!=NULL)
 96             root->xch->prt=root;
 97         tmp->kch=root;
 98         root->prt=tmp;
 99         root->Maintain();
100         tmp->Maintain();
101     }
102     void Splay(Node* root,Node* prt=NULL){
103         while(root->prt!=prt){
104             int k=root->prt->lch==root;
105             if(root->prt->prt==prt)
106                 Rotate(root->prt,k);
107             else{
108                 int d=root->prt->prt->lch==root->prt;
109                 Rotate(k==d?root->prt->prt:root->prt,k);
110                 Rotate(root->prt,d);
111             }
112         }
113     }
114     Node* Build(const std::vector<pr>& v,int l,int r){
115         if(l>r)
116             return NULL;
117         int mid=(l+r)>>1;
118         Node* tmp=new Node(v[mid]);
119         tmp->lch=Build(v,l,mid-1);
120         if(tmp->lch!=NULL)
121             tmp->lch->prt=tmp;
122         tmp->rch=Build(v,mid+1,r);
123         if(tmp->rch!=NULL)
124             tmp->rch->prt=tmp;
125         tmp->Maintain();
126         return tmp;
127     }
128     void PrintTree(Node* root,int deep){
129         for(int i=0;i<deep;i++)
130             fputc(‘ ‘,stderr);
131         if(root==NULL){
132             fprintf(stderr, "(==============================)\n");
133             return;
134         }
135         fprintf(stderr, "(root=0x%X,key=%d,min=%d,size=%d,minp=%d)\n", root,root->key(),root->min(),root->size(),root->minp());
136         PrintTree(root->lch,deep+1);
137         PrintTree(root->rch,deep+1);
138     }
139 public:
140     SplayTree(){
141         this->root=NULL;
142     }
143     void Import(const std::vector<pr>& v){
144         delete this->root;
145         this->root=new Node(std::make_pair(INF,INF));
146         this->root->rch=new Node(std::make_pair(INF,INF));
147         this->root->rch->prt=this->root;
148         this->root->rch->lch=Build(v,0,v.size()-1);
149         this->root->rch->lch->prt=this->root->rch;
150         this->root->rch->Maintain();
151         this->root->Maintain();
152     }
153     void Reverse(int l,int r){
154         this->Splay(this->Kth(l-1));
155         this->Splay(this->Kth(r+1),this->root);
156         this->root->rch->lch->Flip();
157     }
158     Node* Kth(int pos){
159         pos++;
160         Node* root=this->root;
161         while(root!=NULL){
162             root->PushDown();
163             int k=root->lch->size()+1;
164             if(pos<k)
165                 root=root->lch;
166             else if(pos==k)
167                 return root;
168             else{
169                 pos-=k;
170                 root=root->rch;
171             }
172         }
173         return NULL;
174     }
175     void Modify(int pos,pr d){
176         Node* tmp=this->Kth(pos);
177         tmp->k=d;
178         while(tmp!=NULL){
179             tmp->Maintain();
180             tmp=tmp->prt;
181         }
182     }
183     int MinPos(){
184         int ans=0;
185         Node* root=this->root;
186         while(root->minp()!=-1){
187             root->PushDown();
188             if(root->minp()){
189                 ans+=root->lch->size()+1;
190                 root=root->rch;
191             }
192             else
193                 root=root->lch;
194         }
195         ans+=root->lch->size()+1;
196         return ans-1;
197     }
198     void Print(){
199         this->PrintTree(this->root,0);
200     }
201 };
202
203 int main(){
204     int n,tmp;
205     freopen("roboticsort.in","r",stdin);
206     freopen("roboticsort.out","w",stdout);
207     SplayTree* t=new SplayTree();
208     std::vector<pr> v;
209     scanf("%d",&n);
210     while(n!=0){
211         v.clear();
212         for(int i=0;i<n;i++){
213             scanf("%d",&tmp);
214             v.push_back(std::make_pair(tmp,i));
215         }
216         t->Import(v);
217         for(int i=1;i<=n;i++){
218             int p=t->MinPos();
219             printf("%d ",p);
220             t->Reverse(i,p);
221             t->Modify(i,std::make_pair(INF,i));
222         }
223         putchar(‘\n‘);
224         scanf("%d",&n);
225     }
226     return 0;
227 }

Backup

时间: 2024-08-26 17:07:35

[BZOJ 1552] 排序机械臂的相关文章

[CQOI2014]排序机械臂

洛谷P3165 [CQOI2014]排序机械臂 https://www.luogu.org/problem/show?pid=3165 题目描述 为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂.它遵循一个简单的排序规则,第一次操作找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序:第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终所有的物品都会被排好序. 上图给出_个示例,第_次操作前,菝低的物品在位置4,于是把第1至4的物品反序:第二次

洛谷P3165 [CQOI2014]排序机械臂

题目描述 为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂.它遵循一个简单的排序规则,第一次操作找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序:第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终所有的物品都会被排好序. 上图给出_个示例,第_次操作前,菝低的物品在位置4,于是把第1至4的物品反序:第二次操作前,第二低的物品在位罝6,于是把第2至6的物品反序... 你的任务便是编写一个程序,确定一个操作序列,即每次操作前第i低的物品所在位

P3165 [CQOI2014]排序机械臂

P3165 [CQOI2014]排序机械臂 题目描述 为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂.它遵循一个简单的排序规则,第一次操作找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序:第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终所有的物品都会被排好序. 上图给出_个示例,第_次操作前,菝低的物品在位置4,于是把第1至4的物品反序:第二次操作前,第二低的物品在位罝6,于是把第2至6的物品反序... 你的任务便是编写一个程序,确定

【BZOJ3506】排序机械臂(Splay)

[BZOJ3506]排序机械臂(Splay) 题面 神TMBZOJ没有题面,感谢SYC的题面 洛谷的题面也不错 题解 对于每次旋转的物体 显然可以预处理出来 现在只要模拟旋转操作就行了 至于在哪里放标记的问题 我只在第K大放会鬼.. 所以在Splay里面也放了一次(和LCT一样的) 然而我每次都把排到了正确位置的元素直接给删掉了... 所以跑的很慢很慢... #include<iostream> #include<cstdio> #include<cstdlib> #i

LibreOJ2241 - 「CQOI2014」排序机械臂

Portal Description 给出一个\(n(n\leq10^5)\)个数的序列\(\{a_n\}\),对该序列进行\(n\)次操作.若在第\(i\)次操作前第\(i\)小的数在\(p_i\)位置,则翻转区间\([i,p_i]\).易知\(n\)次操作后序列会变为升序.求出每一次的\(p_i\). Solution splay. 题里的\(a_i\)是会重复的...所以先离散化一波,相同的数按位置排名.然后根据初始位置建一棵splay,每次将\(i\)旋转到根求左子树大小即可,区间翻转用

【BZOJ】【1552】【Cerc2007】robotic sort / 【3506】【CQOI2014】排序机械臂

Splay 离散化+Splay维护序列…… 好吧主要说一下我做这道题遇到的几个错误点: 1.离散化 2.由于找到的这个数的位置一定是大于等于 i 的,所以其实在把它splay到根以后,i 结点只能splay到它的左儿子,而不是右儿子……而且相应的,代表这个区间的应该是c[c[root][0]][1]呃……就是root的左儿子的右儿子= =整个反了一下……好吧不要在意这些细节,我也不知道为什么我突然要反过来写[捂脸熊] 3.进行了修改操作以后一定要立即splay修改的结点!在这题中就是当找到最小结

BZOJ 3506 CQOI 2014 排序机械臂 Splay

题目大意:给出一个序列,给出一种排序方式,模拟这种排序方式排序,并输出每次找到的节点的位置. 思路:它让你做什么你就做什么,无非就是个Reverse,很简单.注意一下排序的时候权值相等的情况就行了. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define INF 0x3f3f3f3f using

【BZOJ-1552&amp;3506】robotic sort&amp;排序机械臂 Splay

1552: [Cerc2007]robotic sort Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 806  Solved: 329[Submit][Status][Discuss] Description Input 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000.第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. Output 输出共一行,N个用空格隔开的正整数P1,P2,P3…Pn,Pi表示第i次操作

Luogu P3165 [CQOI2014]排序机械臂

先讲一下和这题一起四倍经验的题: Luogu P4402 [Cerc2007]robotic sort 机械排序 SP2059 CERC07S - Robotic Sort UVA1402 Robotic Sort 这题作为一道十分经典的平衡树维护序列的问题,自然是值得一做的了. 写完翻了下题解发现都是写Splay的dalao,少有的暴力FHQ_Treap党还是用指针实现的. 所以这里略微讲解下数组实现的FHQ_Treap好了,感觉写起来比Splay舒服些. 首先我们要抽象化一下题意:给你\(n