[hdu1890 Robotic Sort]Splay Tree

题意:n个数排成一列,每次选择序列中的最小值(如果有多个,取原始位置最小的),把它和它前面的所有数翻转,然后把这个数从序列中删去。输出每次选择的最小值的下标。
思路:
* Splay Tree是一棵平衡树,在平衡的基础上加上独有的伸展操作,Splay能快速实现诸如区间翻转,区间切割等等普通数据结构力不从心的操作。一棵二叉树,如果用作数据检索,会把数据作为key,中序遍历后得到全部数据的有序排列,而Splay在解决区间问题时,会以数据的下标作为key,中序遍历为数据本来的顺序。在此基础上,伸展操作将不会改变数据本来的顺序,而能达到将一个区间的数据”搬到“一棵子树上的目的,然后对子树进行操作即可。
* 这个题的思路就应该很清晰了,每个节点需要维护几个东西,包括size、子树的最小值的索引、是否翻转、值、原始位置以及parent、两个son的索引。每次找到最小值 V 后,把它splay到根,根据它的左子树的大小可以得到最小值下标,然后找比它大的最小值splay到根,删掉最小值V,把标记放到它的左儿子上,该更新的都要更新。标记采用延迟标记法,跟线段树一样。

#include <bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#include "local.h"
#endif // ONLINE_JUDGE

#define pb(x)                   push_back(x)
#define mp(x, y)                make_pair(x, y)
#define all(a)                  (a).begin(), (a).end()
#define mset(a, x)              memset(a, x, sizeof(a))
#define mcpy(a, b)              memcpy(a, b, sizeof(b))
#define up(a, b)                for (int a = 0; a < (b); a ++)
#define down(a, b)              for (int a = b - 1; (a) >= 0; a --)
#define rep(i, a, b)            for (int i = a; i <= (b); i ++)
#define rrep(i, a, b)           for (int i = a; i >= (b); i --)
#define cas()                   int T, cas = 0; cin >> T; while (T --)
#define printCas(ch)            printf("Case #%d:%c", ++ cas, ch)
#define watch(ele)              cout << ele << endl
#define in(a)                   scanf("%d", &a)

typedef long long ll;
typedef pair<int, int> pii;

template<typename T>bool umax(T&a, const T&b){return a<b?(a=b,true):false;}
template<typename T>bool umin(T&a, const T&b){return b<a?(a=b,true):false;}

const int N = 1e5 + 7;
#define ls s[0]
#define rs s[1]
struct Node {
    int v, index, sz, flip;
    Node *f, *s[2], *minn;
};
Node node[N], *root;
int cnt;
Node* newNode(int v, int index, Node *f) {
    Node *o = node + cnt ++;
    o->v = v;
    o->index = index;
    o->ls = o->rs = 0;
    o->f = f;
    o->minn = o;
    o->sz = 1;
    o->flip = 0;
    return o;
}

void flip(Node *o) {
    o->flip ^= 1;
    swap(o->ls, o->rs);
}
bool smaller(Node *a, Node *b) {
    return a->v == b->v? a->index < b->index : a->v < b->v;
}
void pushup(Node *o) {
    o->sz = 1;
    if (o->ls) o->sz += o->ls->sz;
    if (o->rs) o->sz += o->rs->sz;
    o->minn = o;
    if (o->ls && smaller(o->ls->minn, o->minn)) o->minn = o->ls->minn;
    if (o->rs && smaller(o->rs->minn, o->minn)) o->minn = o->rs->minn;
}
void pushdown(Node *o) {
    if (o->flip == 0) return;
    if (o->ls) flip(o->ls);
    if (o->rs) flip(o->rs);
    o->flip = 0;
}
int dir(Node *o) {
    return o->f->rs == o;
}
void rotate(Node *o) {
    int diro = dir(o);
    Node *f = o->f;
    if (o->s[diro ^ 1]) o->s[diro ^ 1]->f = f;
    f->s[diro] = o->s[diro ^ 1];
    if (f->f) {
        if (f->f->ls == f) f->f->s[0] = o;
        else f->f->s[1] = o;
    }
    o->f = f->f;
    f->f = o;
    o->s[diro ^ 1] = f;
    pushup(f);
    pushup(o);
}
void splay(Node *o) {
    while (o->f) {
        if (o->f->f && dir(o) == dir(o->f)) rotate(o->f);
        rotate(o);
    }
    root = o;
}

void insert(int v, int index) {
    Node *o = root;
    if (!root) root = newNode(v, index, 0);
    else splay(root->rs = newNode(v, index, root));
}
void findMin() {
    Node *o = root;
    while (o->minn != o) {
        pushdown(o);
        if (o->ls && o->minn == o->ls->minn) o = o->ls;
        else o = o->rs;
    }
    pushdown(o);
    splay(o);
}
void findNext() {
    Node *o = root->rs;
    while (o->ls) {
        pushdown(o);
        o = o->ls;
    }
    pushdown(o);
    splay(o);
}
int go() {
    findMin();
    Node *o = root;
    findNext();
    if (o->ls) o->ls->f = root;
    root->ls = o->ls;
    if (o->ls) flip(o->ls);
    pushup(root);
    return o->ls? o->ls->sz : 0;
}
int n, a[N];
int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    while (cin >> n, n) {
        up(i, n) in(a[i]);
        root = 0;
        cnt = 0;
        up(i, n) insert(a[i], i);
        insert(1 << 30, n);
        up(i, n) printf("%d%c", i + 1 + go(), i == n - 1? '\n' : ' ');
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值