首页 > 代码库 > [CQOI2014]排序机械臂
[CQOI2014]排序机械臂
洛谷P3165 [CQOI2014]排序机械臂
https://www.luogu.org/problem/show?pid=3165
题目描述
为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂。它遵循一个简单的排序规则,第一次操作找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序;第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终所有的物品都会被排好序。
上图给出_个示例,第_次操作前,菝低的物品在位置4,于是把第1至4的物品反序;第二次操作前,第二低的物品在位罝6,于是把第2至6的物品反序...
你的任务便是编写一个程序,确定一个操作序列,即每次操作前第i低的物品所在位置Pi,以便机械臂工作。需要注意的是,如果有高度相同的物品,必须保证排序后它们的相对位置关系与初始时相同。
输入输出格式
输入格式:
第一行包含正整数n,表示需要排序的物品数星。
第二行包含n个空格分隔的整数ai,表示每个物品的高度。
输出格式:
输出一行包含n个空格分隔的整数Pi。
输入输出样例
63 4 5 1 6 2
4 6 4 5 6 6
基本框架:splay 区间反转+查询
流程:
1、数据排序
排序规则:高度小的在前,若高度相同,位置靠前的在前
2、建立平衡树,平衡树中直接存储物品的编号,即加上两个虚拟节点后,平衡树中的节点x,对应初始序列位置为x的点;平衡树的中序遍历,对应本次排序后的物品顺序
物品的高度只在排序中有用,自建树开始高度完全没有用了
3、对于输出物品位置p,将物品旋转至根节点,输出左子树大小+1即可
4、区间反转
注意维护反转标记
#include<cstdio>#include<algorithm>#define N 100005using namespace std;int n,fa[N],siz[N],ch[N][2],root,tag[N];struct node{ int i,k;}a[N];inline bool cmp(node p,node q){ if(p.k<q.k) return 1; if(p.k==q.k) return p.i<q.i; return 0;}inline void update(int k){ siz[k]=siz[ch[k][0]]+siz[ch[k][1]]+1;} inline void build(int l,int r,int f){ if(l>r) return; int mid=l+r>>1; if(mid<f) ch[f][0]=mid; else ch[f][1]=mid; siz[mid]=1;fa[mid]=f; if(l==r) return; build(l,mid-1,mid); build(mid+1,r,mid); update(mid);}inline void down(int k){ tag[k]^=1; if(ch[k][0]) tag[ch[k][0]]^=1; if(ch[k][1]) tag[ch[k][1]]^=1; swap(ch[k][0],ch[k][1]);}inline void rotate(int x,int & goal){ int y=fa[x],z=fa[y],kind=ch[y][1]==x; if(y==goal) goal=x; else ch[z][ch[z][1]==y]=x; ch[y][kind]=ch[x][kind^1];ch[x][kind^1]=y; fa[y]=x;fa[x]=z;fa[ch[y][kind]]=y; update(y);}inline void splay(int x,int & goal){ while(x!=goal) { int y=fa[x],z=fa[y]; if(tag[z]) down(z); if(tag[y]) down(y); if(tag[x]) down(x); if(y!=goal) { if(ch[y][1]==x^ch[z][1]==y) rotate(x,goal); else rotate(y,goal); } rotate(x,goal); update(x); }} inline int find(int now,int k){ if(tag[now]) down(now); int l=ch[now][0],r=ch[now][1]; if(siz[l]+1==k) return now; if(k<=siz[l]) return find(l,k); return find(r,k-siz[l]-1);}inline void reverse(int l,int r){ int x=find(root,l-1),y=find(root,r+1); splay(x,root);splay(y,ch[x][1]); tag[ch[y][0]]^=1;}int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) {scanf("%d",&a[i+1].k); a[i+1].i=i+1;} a[1].i=1;a[n+2].i=n+2;a[n+2].k=2000000001; sort(a+1,a+n+3,cmp); build(1,n+2,0);root=n+3>>1; int p; for(int i=2;i<=n;i++) { splay(a[i].i,root); p=siz[ch[root][0]]+1; printf("%d ",p-1); reverse(i,p); } printf("%d",n);}
3个错误:
①、标记下传
a.错误代码:
inline void down(int k)
{
tag[k]^=1;
tag[ch[k][0]]^=1;tag[ch[k][1]]^=1;
swap(ch[k][0],ch[k][1]);
}
错因:没有判断是否有左右孩子,如果没有孩子,为0,标记下穿至0号节点,在区间范围锁定时,如果l=1,查找0号节点,因为建树的时候,最初的父节点为0,所以就会误将0号节点的tag下传
b.splay中没有标记下传
if(tag[z]) down(z);
if(tag[y]) down(y);
if(tag[x]) down(x);
以前写的那个splay模板因为splay在find之后,而find标记下传了,所以splay不需要下传
②、排序
错误代码:
inline bool cmp(node p,node q)
{
return p.k<=q.k;
}
不能完成k相同时位置靠前的在前
③、查找位置
开始写了一个splay模板中的查找代码
错误代码:
inline int findpos(int now,int x)
{
if(x==now) return 1;
if(tag[now]) down(now);
if(x<now) return findpos(ch[now][0],x);
return siz[ch[now][0]]+1+findpos(ch[now][1],x);
}
错因:虽然一开始建立平衡树是按编号的大小顺序建立的,但建立后,区间的反转时平衡树的节点间没有大小关系,所以不能根据now和x的大小查找x
其实只要将x旋转至根节点,输出左子树大小+1即可
思维太死板、模式化
[CQOI2014]排序机械臂