JZOJ3599. 【CQOI2014】排序机械臂

splay裸题不解释。。。

按照位置为关键字排序,维护子树大小和区间最小。
之后每找到一个最小的值,就把它区间旋到边上,然后放到最顶上扔掉

写得相对比较优美

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(x,y) (x<y?x:y)
using namespace std;

int n,i,j,k,l;
int a[100002];
int b[100002];
int c[100002];
int ch[100002][2];
int f[100002];
int s[100002];
int m[100002];
bool rev[100002];

void qsort(int l,int r)
{
    int i,j,k,mid,mid2;

    i=l;
    j=r;
    mid=b[(l+r)/2];
    mid2=c[(l+r)/2];

    while (i<=j)
    {
        while ((b[i]<mid) || ((b[i]==mid) && (c[i]<mid2))) i++;
        while ((b[j]>mid) || ((b[j]==mid) && (c[j]>mid2))) j--;

        if (i<=j)
        {
            k=b[i];
            b[i]=b[j];
            b[j]=k;
            k=c[i];
            c[i]=c[j];
            c[j]=k;

            i++;
            j--;
        }
    }

    if (l<j)
    qsort(l,j);

    if (i<r)
    qsort(i,r);

    return ;
}

void update(int t)
{
    s[t]=1+s[ch[t][0]]+s[ch[t][1]];
    m[t]=min(min(m[ch[t][0]],m[ch[t][1]]),a[t]);
}

void downtag(int t)
{
    if (rev[t])
    {
        int i=ch[t][0];
        ch[t][0]=ch[t][1];
        ch[t][1]=i;

        rev[ch[t][0]]^=1,rev[ch[t][1]]^=1,rev[t]=0;
    }
}

void maketree(int t,int l,int r)
{
    int mid=(l+r)/2;

    if (l!=r)
    {
        if (l<mid)
        maketree(mid,l,mid-1);
        else
        ch[mid][0]=n+1;
        if (mid<r)
        maketree(mid,mid+1,r);
    }

    f[mid]=t;
    if (!ch[t][0])
    ch[t][0]=mid;
    else
    ch[t][1]=mid;

    update(mid);
}

void rot(int t)
{
    downtag(t);

    int x=(ch[f[t]][0]==t)?0:1;
    f[ch[t][x^1]]=f[t],ch[f[t]][x]=ch[t][x^1];
    ch[t][x^1]=f[t];
    if ((f[f[t]]) && (ch[f[f[t]]][0]==f[t])) ch[f[f[t]]][0]=t; else ch[f[f[t]]][1]=t;
    f[t]=f[f[t]],f[ch[t][x^1]]=t;

    update(ch[t][x^1]),update(t);
}

void splay(int t,int x)
{
    while (f[t]!=x)
    {
        if (f[f[t]]==x)
        rot(t);
        else
        {
            if ((ch[f[f[t]]][0]==f[t])^(ch[f[t]][0]==t))
            rot(t),rot(t);
            else
            rot(f[t]),rot(t);
        }
    }
}

void find(int t,int sum,bool bz)
{
    downtag(t);

    if (a[t]==m[t])
    {
        if (i==1)
        sum--;

        if (bz)
        printf("%d ",sum+1+s[ch[t][0]]),k=t;
        splay(t,j);
        return;
    }

    if (m[ch[t][0]]<m[ch[t][1]])
    find(ch[t][0],sum,bz);
    else
    find(ch[t][1],sum+s[ch[t][0]]+1,bz);
}

int main()
{
    memset(m,127,sizeof(m));

    scanf("%d",&n);
    fo(i,1,n)
    scanf("%d",&a[i]),b[i]=a[i],c[i]=i;

    qsort(1,n);

    fo(i,1,n)
    a[c[i]]=i;

    maketree(0,1,n);
    fo(i,1,n)
    if (ch[i][0]>n)
    ch[i][0]=0;

    ch[0][0]=0;
    ch[0][1]=(1+n)/2;
    f[ch[0][1]]=0;
    j=0;
    fo(i,1,n-1)
    {
        downtag(ch[j][1]);
        find(ch[j][1],s[ch[j][0]]+1,1);

        if (ch[ch[j][1]][1])
        {
            l=ch[ch[j][1]][1];
            downtag(l);
            while (ch[l][0])
            l=ch[l][0],downtag(l);

            splay(l,j);
            rev[k]=1;
        }
        else
        rev[k]=1;

        splay(k,0);
        j=k;
    }

    printf("%d\n",n);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值