【HDU 1890】Robotic Sort【splay】

13 篇文章 0 订阅
3 篇文章 0 订阅

题意:给出一段序列,每次找出第i的大的数与它本来的位置一段序列进行反转, 相当于一次排序,每次输出第i大的数的位置。

思路:相当于区间的反转,我们对于整个序列建立一棵伸展树,每次把第i大的数字旋转到根,其的左孩子个数就相当于它在原序列的位置,然后每次进行区间反转,splay的区间操作会在另一篇文章中提到,其他的操作与线段树的一致。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define N 500006
#define INF 0x3f3f3f3f
#define lc (tr[id].c[0])
#define rc (tr[id].c[1])
#define KEY tr[tr[root].c[1]].c[0]

int data[N/5];

struct Num {
    int val, id;
    bool operator<(const Num a)const {
        if (val == a.val) return id < a.id;
        return val < a.val;
    }
}So[N/5];

struct Tr
{
    int fa, sum, val, c[2], lz;
}tr[N];

int tot, root, n;

int newtr(int k, int f, int pos) {
    tr[tot].sum = 1, tr[tot].val =  k;
    tr[tot].c[0] = tr[tot].c[1] = -1;
    tr[tot].lz = 0;
    tr[tot].fa = f;
    return tot++;
}

void Push(int id) {
   // if (id == -1) return;
    int lsum, rsum;
    lsum = (lc == -1)?0:tr[lc].sum;
    rsum = (rc == -1)?0:tr[rc].sum;
    tr[id].sum = lsum+rsum+1;
}

int build(int l, int r, int f) {
    if (r < l) return-1;
    int mid = l+r>>1;
    int ro = newtr(mid, f, mid);
    data[mid] = ro;
    tr[ro].c[0] = build(l, mid-1, ro);
    tr[ro].c[1] = build(mid+1, r, ro);
    Push(ro);
    return ro;
}

void lazy(int id) {
   // if (id == -1) return;
    if (tr[id].lz) {
        swap(lc, rc);
        tr[lc].lz ^= 1, tr[rc].lz ^= 1;
        tr[id].lz = 0;
    }
}

void Rotate(int x, int k) {
    if (tr[x].fa == -1) return;
    int fa = tr[x].fa, w;
    lazy(fa), lazy(x);
    tr[fa].c[!k] = tr[x].c[k];
    if (tr[x].c[k] != -1) tr[tr[x].c[k]].fa = fa;
    tr[x].fa = tr[fa].fa, tr[x].c[k] = fa;
    if (tr[fa].fa != -1) {
        w = tr[tr[fa].fa].c[1]==fa;
        tr[tr[fa].fa].c[w] = x;
    }
    tr[fa].fa = x;
    Push(fa);
    Push(x);
}

void Splay(int x, int goal) {
    if (x == -1) return;
    lazy(x);
    while (tr[x].fa != goal) {
        int y = tr[x].fa;
        lazy(tr[y].fa), lazy(y), lazy(x);
        bool w = x==tr[y].c[1];
        if (tr[y].fa != goal && w == (y==tr[tr[y].fa].c[1]))
            Rotate(y, !w);
        Rotate(x, !w);
    }
    if (goal == -1) root = x;
    Push(x);
}

int find(int k) {
    int id = root;
    while (id != -1) {
        lazy(id);
        int lsum = (lc==-1)?0:tr[lc].sum;
        if (lsum >= k) {
            id = lc;
        }
        else if (lsum+1 == k) break;
        else {
            k = k-lsum-1;
            id = rc;
        }
    }
    return id;
}

int Getnext(int id) {
    lazy(id);
    int p = tr[id].c[1];
    if (p == -1) return id;
    lazy(p);
    while (tr[p].c[0] != -1) {
        p = tr[p].c[0];
        lazy(p);
    }
    return p;
}

int main() {
    int m, l, r, k, d, i;
    while (~scanf("%d", &n), n) {
        for (i = 1;i <= n;i++) {
            scanf("%d",  &So[i].val);
            So[i].id = i;
        }
        sort(So+1, So+n+1);
        So[0].id = 0;
        tot = 0;
        root = build(0, n+1, -1);
        for (i = 1;i <= n;i++) {
            int ro = data[So[i].id], ne;
            Splay(ro, -1);
            d = tr[tr[root].c[0]].sum;
            l = data[So[i-1].id];
            ne = Getnext(ro);
            Splay(l, -1), Splay(ne, root);
            lazy(root), lazy(tr[root].c[1]);
            tr[KEY].lz ^= 1;
            if (i != 1) printf(" ");
            printf("%d", d);
        }
        puts("");
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值