HDU1890-SplayTree

题目描述
给你一个序列,第i次操作是把第i个数到第i小的数这段区间翻转,即第i次操作把第i小的数一次翻转到第i个位置,最终序列会变成升序的(注意:如果有相同的数,那么在初始序列中靠前的在最终序列也靠前,即相同的数相对位置不变),每次操作前输出第i小的数所在的位置。
题解:
如果每次直接找整个数列第i小的数,需要树套树,其实,只要每次把第i个数翻转到第i个位置后,下一次操作需要翻转的数就是剩下的数中最小的,而只要维护两个标记,分别表示子树中最小的数和最小的数的位置,就可以实现找到最小的数,所以,只要每次翻转后都把最小的数删除即可。要维护的是序列,只要用SplayTree即可。
代码如下:

#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn 100006
using namespace std;
struct node{
    node* ch[2];
    int key,flip,cnt,M,Min;
    void maintain(){
        cnt=ch[0]->cnt+1+ch[1]->cnt;
        Min=min(min(ch[0]->Min,key),ch[1]->Min);
        if(Min==ch[0]->Min)M=ch[0]->M;else
        if(Min==key)M=ch[0]->cnt+1;else
                    M=ch[0]->cnt+1+ch[1]->M;
    }
    void add(){
        M=cnt-M+1;swap(ch[0],ch[1]);flip^=1;
    }
    void pushdown(){
        if(flip)ch[0]->add(),ch[1]->add(),flip=0;
    }
    int cmp(int &k){
        if(k< ch[0]->cnt+1)return 0;
        if(k==ch[0]->cnt+1)return -1;
        k-=ch[0]->cnt+1;return 1;
    }
}nul;
struct data{
    int x,id;
    bool operator <(const data&b)const{
        return id<b.id;
    }
}a[maxn];
typedef node* pnode;
pnode null=&nul;
int _read(){
    int sum=0;char ch=getchar();
    while(!(ch>='0'&&ch<='9'))ch=getchar();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=getchar();
    return sum;
}
pnode newnode(int k){
    pnode p=new node;p->key=k;p->cnt=1;p->flip=0;p->ch[0]=p->ch[1]=null;
    p->Min=k;p->M=1;
    return p;
}
void rot(pnode &p,int d){
    pnode k=p->ch[d^1];p->ch[d^1]=k->ch[d];k->ch[d]=p;
    p->maintain();k->maintain();p=k;
}
void splay(pnode &p,int k){
    p->pushdown();
    int d1=p->cmp(k);
    if(d1!=-1){
        p->ch[d1]->pushdown();int d2=p->ch[d1]->cmp(k);
        if(d2!=-1){
            splay(p->ch[d1]->ch[d2],k);
            if(d1==d2)rot(p,d1^1);
                 else rot(p->ch[d1],d2^1);
        }
        rot(p,d1^1);
    }
}
pnode build(int l,int r,data *arr){
    if(l>r)return null;
    int mid=(l+r)>>1;
    pnode p=newnode((arr+mid)->x);
    p->ch[0]=build(l,mid-1,arr);p->ch[1]=build(mid+1,r,arr);
    p->maintain();
    return p;
}
bool cmp(data i,data j){
    return (i.x<j.x)|((i.x==j.x)&&(i.id<j.id));
}
int n;
int main(){
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
    null->ch[0]=null->ch[1]=null;null->cnt=0;null->Min=1e9;
    while(n=_read()){
        for(int i=1;i<=n;i++)a[i].x=_read(),a[i].id=i;a[0].x=a[n+1].x=1e9;
        sort(a+1,a+1+n,cmp);
        for(int i=1;i<=n;i++)a[i].x=i;
        sort(a+1,a+1+n);
        pnode rt=build(0,n+1,a);
        for(int i=1;i<n;i++){
            printf("%d ",rt->M+i-2);
            splay(rt,1);splay(rt->ch[1],rt->ch[1]->M+1);
            rt->ch[1]->ch[0]->add();rt->ch[1]->ch[0]->pushdown();
            splay(rt->ch[1],2);
            delete rt->ch[1]->ch[0];rt->ch[1]->ch[0]=null;rt->ch[1]->maintain();rt->maintain();
        }printf("%d\n",n);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值