洛谷P3165 [CQOI2014]排序机械臂【splay】

题目描述
为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂。它遵循一个简单的排序规则,第一次操作找到高度最低的物品的位置 p1 p 1 ,并把左起第一个物品至 p1 p 1 间的物品 (即区间 [1,p1] [ 1 , p 1 ] 间的物品) 反序;第二次找到第二低的物品的位置 p2 p 2 ,并把左起第二个至 p2 p 2 间的物品 (即区间 [2,p2] [ 2 , p 2 ] 间的物品) 反序……最终所有的物品都会被排好序。
这里写图片描述

上图给出有六个物品的示例,第一次操作前,高度最低的物品在位置 4 ,于是把第一至第四的物品反序;第二次操作前,第二低的物品在位罝六,于是把第二至六的物品反序……

你的任务便是编写一个程序,确定一个操作序列,即每次操作前第 pi p i 低的物品所在位置 pi p i ,以便机械臂工作。需要注意的是,如果有高度相同的物品,必须保证排序后它们的相对位置关系与初始时相同。

输入格式:

第一行包含正整数n,表示需要排序的物品数星。

第二行包含n个空格分隔的整数ai,表示每个物品的高度。

输出格式:

输出一行包含n个空格分隔的整数Pi。

输入样例

6
3 4 5 1 6 2

输出样例

4 6 4 5 6 6

说明

N<=100000
Pi<=10^7


题目分析

比较常规的splay区间翻转题
加入哨兵结点建树
高度排序后一次查询排名并翻转

有两点需要注意
1.每次要先把元素旋转到根得到他的排名在反转区间
所以在这里splay里面也要加push下推
而且 z, y, x都要push

2.高度相同的物体排序后顺序也要相同
读入时要把高度与编号存入结构体再双关键字排序
若直接对高度sort,可能存在不稳定性
手写sort请无视


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=200010;
int n;
struct node{int v,pos;}a[maxn];
int ch[maxn][2],fa[maxn];
int size[maxn],sz,rt;
int lzy[maxn];

bool cmp(node a,node b)
{
    if(a.v==b.v) return a.pos<b.pos;
    return a.v<b.v;
}

void update(int p)
{
    size[p]=size[ch[p][0]]+size[ch[p][1]]+1;
}

inline void push(int p)
{
    if(lzy[p])
    {
        swap(ch[p][0],ch[p][1]);
        lzy[ch[p][0]]^=1; lzy[ch[p][1]]^=1;
        lzy[p]=0;
    }
}

void rotate(int &p,int x)
{
    int y=fa[x],z=fa[y];
    int t=(ch[y][0]==x);
    if(y==p) p=x;
    else if(ch[z][0]==y) ch[z][0]=x;
    else ch[z][1]=x;
    fa[y]=x; fa[ch[x][t]]=y; fa[x]=z;
    ch[y][t^1]=ch[x][t]; ch[x][t]=y;
    update(y); update(x);
}

void splay(int &p,int x)
{
    while(x!=p)
    {
        int y=fa[x],z=fa[y];
        push(z);push(y);push(x);
        if(y!=p)
        {
            if((ch[z][0]==y)^(ch[y][0]==x))rotate(p,x);
            else rotate(p,y);
        }
        rotate(p,x);
    }
}

void build(int p,int ll,int rr)
{
    if(ll>rr) return;
    int mid=ll+rr>>1;
    fa[mid]=p; size[mid]=1;
    ch[p][mid>p]=mid;
    if(ll==rr) return;
    build(mid,ll,mid-1);build(mid,mid+1,rr);
    update(mid); 
}

int find(int p,int k) 
{ 
    push(p);  
    int ss=size[ch[p][0]]; 
    if(k==ss+1) return p;    
    if(k<=ss) return find(ch[p][0],k);
    else return find(ch[p][1],k-ss-1);  
}  

void rev(int ll,int rr)
{
    int x=find(rt,ll-1),y=find(rt,rr+1);
    splay(rt,x);
    splay(ch[rt][1],y); 
    lzy[ch[y][0]]^=1;
}

void solve()
{
    for(int i=2;i<=n;++i)
    {
        int x=a[i].pos; 
        splay(rt,x);
        printf("%d ",size[ch[rt][0]]);
        int ll=i-1,rr=size[ch[rt][0]];
        rev(ll+1,rr+1);
    }
    printf("%d ",n);//最后一个元素的排名必定为n,所以不用查询
}

int main()
{
    n=read(); 
    for(int i=2;i<=n+1;++i){ a[i].v=read(); a[i].pos=i;}
    a[1].v=-1e8; a[1].pos=1;//读入高度信息
    a[n+2].v=1e8; a[n+2].pos=n+2;

    rt=n+3>>1;
    build(rt,1,n+2);

    sort(a+1,a+n+3,cmp);//双关键字排序
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值