【CQOI2014】排序机械臂

Description

这里写图片描述

Solution

用什么

刚看到这道题,哎呀,信心大增:序列的翻转不就是用splay吗!
如果不会splay,详见splay复习小计

怎么做

序列的翻转直接把子树的所有左右儿子调换即可。
然后先排一个序,找到每次要找的数他的位置(splay改变的只是树上的位置,他的下标就是原来的位置),然后把这个数旋转到根节点,输出答案(他目前在树上的位置是他在根节点是左子树的个数+1,注意每次+1要改成+i,因为前面删去了i-1个数)。
接着就要把这个点root删除了,发现直接删除不行,那么我们先找到root右子树中的最左边的一个点x,其实目前的序列中root的右边就是x。找到这个x之后,把x旋转到根节点上,然后把root的做节点与x相连,因为现在root肯定没有右节点,所以不用管右边,那么现在就把x删除掉了。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100007;
int i,j,k,l,n,m,t[maxn][2],size[maxn],f[maxn],root,mi[maxn],w[maxn];
int a[maxn],ans[maxn],biao[maxn],d[maxn];
struct node{
    int a,b;
}b[maxn];
bool cmp(node x,node y){
    return x.a<y.a||x.a==y.a&&x.b<y.b;
}
bool son(int x){
    if(t[f[x]][0]==x)return 0;return 1;
}
void back(int x){
    biao[x]^=1;swap(t[x][0],t[x][1]);
}
void down(int x){
    if(biao[x]){
        if(t[x][0])back(t[x][0]);
        if(t[x][1])back(t[x][1]);
        biao[x]=0;
    }
}
void update(int x){
    size[x]=size[t[x][0]]+size[t[x][1]]+1;
}
void rotate(int x){
    int y=f[x],z=son(x);
    t[y][z]=t[x][1-z];
    if(t[x][1-z])f[t[x][1-z]]=y;f[x]=f[y];
    if(f[x])t[f[x]][son(y)]=x;
    f[y]=x;t[x][1-z]=y;
    update(y);update(x);
}
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 splay(int x,int y){
    remove(x,y);
    while(f[x]!=y){
        if(f[f[x]]!=y)
            if(son(f[x])==son(x))rotate(f[x]);else rotate(x);
        rotate(x);
    }
    if(!y)root=x;
}
void suoga(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 dele(int x){
    f[t[x][0]]=f[x];
    t[f[x]][0]=t[x][0];
    update(f[x]);
}
int build(int l,int r,int fa){
    int mid=(l+r)/2;
    if(l>r)return 0;
    f[mid]=fa;size[mid]=r-l+1;
    if(l==r)return l;
    t[mid][0]=build(l,mid-1,mid);
    t[mid][1]=build(mid+1,r,mid);
    return mid;
}
int main(){
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%d",&a[i]);
        b[i].a=a[i];b[i].b=i;
    }
    sort(b+1,b+1+n,cmp);
    build(1,n+1,0);
    fo(i,1,n){
        splay(b[i].b,0);back(t[root][0]);
        printf("%d",size[t[root][0]]+i);
        if(i!=n)printf(" ");
        suoga(root);
        dele(t[root][0]);
    }
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值