【bzoj3506】【CQOI2014】排序机械臂

Description

有n个物品,每个物品有它的高度a[i],现在我们要采用一种神奇的方法把这n个物品排好序。第x次我们找到第x矮的物品位置p[x],并且把x到p[x]中间的物品翻转。如果有多个一样高度的,位置后的更高。
求所有的p[i]。
n<=10^5

Solution

我们可以模拟题目中的操作。
翻转神马的splay杠杠的。
于是这道题就是一道基础splay序列题。
为了方便查找,我们可以先把所有物品排个序,然后每次把1~i-1翻转,并且把i删除(防止对后面的影响),接着把1~i-1和i+1~n合并。
练一练码力。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 100005
using namespace std;
struct note{int v,w;}a[N];
bool cmp(note x,note y) {return x.v<y.v||x.v==y.v&&x.w<y.w;}
int n,t[N][2],f[N],size[N],rev[N],d[N],ans[N],root;
void updata(int x) {
    size[x]=size[t[x][0]]+size[t[x][1]]+1;
}
int son(int x) {
    if (x==t[f[x]][0]) return 0;else return 1;
}
void back(int x) {
    rev[x]^=1;swap(t[x][0],t[x][1]);
}
void down(int x) {
    if (rev[x]) {
        if (t[x][0]) back(t[x][0]);
        if (t[x][1]) back(t[x][1]);
        rev[x]=0;
    }
}
void remove(int x,int y) {
    if (x==y) return;
    do {
        d[++d[0]]=x;x=f[x];
    } while (x!=y);
    while (d[0]) down(d[d[0]--]);
}
void rotate(int x) {
    int y=f[x],z=son(x);f[x]=f[y];
    if (f[y]) t[f[y]][son(y)]=x;
    t[y][z]=t[x][1-z];
    if (t[x][1-z]) f[t[x][1-z]]=y;
    t[x][1-z]=y;f[y]=x;
    updata(y);updata(x);
}
void splay(int x,int y) {
    remove(x,y);
    while (f[x]!=y) {
        if (f[f[x]]!=y)
            if (son(x)==son(f[x])) rotate(f[x]);
            else rotate(x);
        rotate(x);
    }
    if (!y) root=x;
}
int build(int l,int r,int fa) {
    if (l>r) return 0;
    int m=(l+r)/2;
    f[m]=fa;size[m]=r-l+1;
    if(l==r)return l;
    t[m][0]=build(l,m-1,m);
    t[m][1]=build(m+1,r,m);
    return m;
}
void find(int x) {
    down(x);x=t[x][1];
    while (x) {
        down(x);
        if (t[x][0]) x=t[x][0];
        else break;
    }
    splay(x,0);
}
void del(int x) {
    f[t[x][0]]=f[x];
    t[f[x]][0]=t[x][0];
    updata(f[x]);
}
int main() {
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i].v),a[i].w=i;
    build(1,n+1,0);
    sort(a+1,a+n+1,cmp);
    fo(i,1,n) {
        splay(a[i].w,0);back(t[root][0]);
        printf("%d",size[t[root][0]]+i);
        if (i!=n) printf(" ");
        find(root);del(t[root][0]);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值