[Luogu] 排序机械臂

https://www.luogu.org/problemnew/solution/P3165

预处理

我们会发现一个问题:高度是无序的,而splay中要求有序,否则kth不能正确求解。
不需要求高度,只要求位置。
所以,用结构体存入 高度 与 下标,按高度排序,然后就可以把高度丢一边了(一波sao操作)。
记得头尾添加两个节点。。。
建树
正常 nlogn 可能会被卡常,所以,类似于线段树的建树,分此节点与左右儿子节点。
区间第k大
正常kth
区间翻转
首先,需要把所求的节点(即排序前下标为id的节点) splay 到 root 。
那么答案就是root左孩子的节点个数(因为有哨兵节点,所以+1-1抵消),记为s。
然后,取出 [i+1 , s+1] 这段区间,即:
将i节点 splay 到根,s+2节点 splay 到i的右节点。
再将s+2的左孩子打上翻转标记即可。

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 100010
#define MAX 999999999//最值
using namespace std;
int n,size=1,root=0;
struct node {
    int x,id;
} b[MAXN];
namespace splay {
    struct Splay {
        int f,s,flag,son[2];
        int v;
    } a[MAXN];
    inline void clean(int rt) { //清空节点
        a[rt].son[0]=a[rt].son[1]=a[rt].f=a[rt].s=a[rt].flag=a[rt].v=0;
    }
    inline void pushup(int rt) { //上传
        if(!rt)return;
        a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+1;
    }
    inline void pushdown(int rt) { //标记下传
        if(!rt||!a[rt].flag)return;//记得这句
        a[a[rt].son[0]].flag^=1;
        a[a[rt].son[1]].flag^=1;
        a[rt].flag^=1;
        swap(a[rt].son[0],a[rt].son[1]);
    }
    inline void turn(int rt,int k) { //旋转
        int x=a[rt].f,y=a[x].f;
        a[x].son[k^1]=a[rt].son[k];
        if(a[rt].son[k])a[a[rt].son[k]].f=x;
        a[rt].f=y;
        if(y)a[y].son[a[y].son[1]==x]=rt;
        a[x].f=rt;
        a[rt].son[k]=x;
        pushup(x);
        pushup(rt);
    }
    void splay(int rt,int ancestry) { //伸展
        while(a[rt].f!=ancestry) {
            int x=a[rt].f,y=a[x].f;
            pushdown(y);
            pushdown(x);
            pushdown(rt);//每次都要下传
            if(y==ancestry)turn(rt,a[x].son[0]==rt);
            else {
                int k=a[y].son[0]==x?1:0;
                if(a[x].son[k]==rt) {
                    turn(rt,k^1);
                    turn(rt,k);
                } else {
                    turn(x,k);
                    turn(rt,k);
                }
            }
        }
        if(ancestry==0)root=rt;
    }
    inline int newnode(int x) { //建立新节点
        int rt=size++;
        clean(rt);
        a[rt].v=x;
        a[rt].s=1;
        return rt;
    }
    int buildtree(int l,int r) { //建树
        if(l>r)return 0;
        int mid=l+r>>1,lson=0,rson=0;
        lson=buildtree(l,mid-1);
        int rt=newnode(b[mid].x);
        rson=buildtree(mid+1,r);
        a[rt].son[0]=lson;
        a[rt].son[1]=rson;
        if(lson)a[lson].f=rt;
        if(rson)a[rson].f=rt;
        pushup(rt);//一定要有这句!
        return rt;
    }
    int kth(int rt,int k) { //第k大值
        if(a[rt].s<k)return 0;
        while(1) {
            pushdown(rt);//下传
            int y=a[rt].son[0];
            if(k>a[y].s+1) {
                k-=a[y].s+1;
                rt=a[rt].son[1];
            } else if(k<=a[y].s)rt=y;
            else return rt;
        }
    }
    inline void reverge(int i) { //区间翻转
        splay(b[i].id+1,0);//记得加1(有哨兵节点)
        int s=a[a[root].son[0]].s;
        printf("%d ",s);
        int front=kth(root,i),next=kth(root,s+2);
        splay(front,0);
        splay(next,front);
        a[a[next].son[0]].flag^=1;//打上标记
    }
}
inline int read() {
    int date=0,w=1;
    char c=0;
    while(c<'0'||c>'9') {
        if(c=='-')w=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        date=date*10+c-'0';
        c=getchar();
    }
    return date*w;
}
bool cmp(const node &x,const node &y) {
    if(x.x==y.x)return x.id<y.id;
    return x.x<y.x;
}
void init() { //预处理+读入+工作
    n=read();
    for(int i=1; i<=n; i++) {
        b[i].x=read();
        b[i].id=i;
    }
    b[0].x=-MAX;
    b[0].id=1;
    b[n+1].x=MAX;
    b[n+1].id=n+1;//两个哨兵节点
    sort(b+1,b+n+1,cmp);
    root=splay::buildtree(0,n+1);
    for(int i=1; i<=n-1; i++)splay::reverge(i);
    printf("%d\n",n);//最后一个一定是 n
}
int main() {
    init();
    return 0;
}

 

posted @ 2018-05-08 10:55  xayata  阅读(168)  评论(0编辑  收藏  举报