首页 > 代码库 > [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。

 

输入输出样例

输入样例#1:
63 4 5 1 6 2
输出样例#1:
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]排序机械臂