uva 11922 Permutation Transformer(Splay tree,懒标记传递)

wiki

http://zh.wikipedia.org/wiki/%E4%BC%B8%E5%B1%95%E6%A0%91

关于Splay tree效率分析(包括三种旋转方式图解):

http://www.cs.waikato.ac.nz/Teaching/COMP317B/Week_6/Splay_tree.html

注意,Splay tree的三种旋转中,x的父节点不是root且三点不同线的情况,是在祖父节点做两次同样的旋转。


取出一段区间放到末尾,直接用splay tree可以完成。要做翻转则用到lazy mark。预处理要先加一个虚拟节点,否则a=1时,不满足split的参数要求。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <set>
#include <limits>
using namespace std;

#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define REP(i, s, t) for(int (i)=(s);(i)<=(t);++(i))
#define UREP(i, s, t) for(int (i)=(s);(i)>=(t);--(i))
#define REPOK(i, s, t, o) for(int (i)=(s);(i)<=(t) && (o);++(i))

#define MAXN 100
#define MAXM 10000
#define MOD 10000007

#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398

typedef long long LL;

const double maxdouble = numeric_limits<double>::max();
const double eps = 1e-10;
const int INF = 0x7FFFFFFF;

// Splay Tree 伸展树
// 把一棵Splay tree看作一段区间 其中每一棵子树都是一段小区间
// 不一定要满足BST的性质 可以维护一种顺序关系 例如让某段区间翻转
// 可以加上lazy mark
typedef struct Node {
    Node *ch[2];
    int v;  // value
    int s;  // size 名次树中子树结点个数
    int flip;

    int cmp(int k) const {
        int d = k - ch[0]->s;
        if (d == 1) return -1;
        return d <= 0 ? 0 : 1;
    }
    void maintain() {
        s = ch[0]->s+ch[1]->s+1;
    }
    void pushdown() {
        if (flip) {
            flip = 0;
            swap(ch[0], ch[1]);
            ch[0]->flip = !ch[0]->flip;
            ch[1]->flip = !ch[1]->flip;
        }
    }
}SplayTreeNode;

const int maxnode = 100000 + 10;
SplayTreeNode nullSpace; // null指向的内存
SplayTreeNode* null = &nullSpace;
struct SplayTree {
    int n;
    // seq 是预先分配的储存节点的空间 这样不用动态申请
    SplayTreeNode seq[maxnode+5];
    SplayTreeNode* root;

    void rotate(SplayTreeNode* &o, int d) {
        SplayTreeNode* k = o->ch[d^1];
        o->ch[d^1] = k->ch[d];
        k->ch[d] = o;
        o->maintain();
        k->maintain();
        o = k;
    }
    void splay(SplayTreeNode* &o, int k) {
        o->pushdown();
        int d = o->cmp(k);
        if (d == 1) k -= o->ch[0]->s + 1;
        if (d != -1) {
            SplayTreeNode* p = o->ch[d];
            p->pushdown();
            int d2 = p->cmp(k);
            int k2 = d2 == 0 ? k : k - p->ch[0]->s - 1;
            if (d2 != -1) {
                splay(p->ch[d2], k2);
                if (d == d2) { // 三点共线
                    rotate(o, d^1);
                } else {
                    rotate(o->ch[d], d);
                }
            }
            rotate(o, d^1);
        }
    }
    // 合并两棵splay tree
    // 先将第一棵树的最大节点伸展 结果是根节点没有右子树
    // left不能为null
    SplayTreeNode* merge(SplayTreeNode* &left, SplayTreeNode* &right) {
        splay(left, left->s);
        left->ch[1] = right;
        left->maintain();
        return left;
    }
    // 把o的前k小结点放在left里,其他的放在right里。
    // !!!1<=k<=o->s。当k=o->s时,right=null
    void split(SplayTreeNode* o, int k, SplayTreeNode* &left, SplayTreeNode* &right) {
        splay(o, k);
        left = o;
        right = o->ch[1];
        if (o->ch[1] != null)
            o->ch[1] = null;
        left->maintain();
    }
    Node* build(int sz) {
        if (!sz) return null;
        Node* L = build(sz/2);
        Node* o = &seq[++n];
        o->v = n;
        o->ch[0] = L;
        o->ch[1] = build(sz - sz/2 - 1);
        o->flip = o->s = 0;
        o->maintain();
        return o;
    }

    void init(int sz) {
        n = 0;
        null->s = 0;
        root = build(sz);
    }
};

SplayTree tree;

void print(SplayTreeNode* o) {
    if (o->flip)
        o->pushdown();
    if (o->ch[0] != null) print(o->ch[0]);
    if (o->v != 1)
        printf("%d\n",o->v-1);
    if (o->ch[1] != null) print(o->ch[1]);
}

void debug(SplayTreeNode *x) {
    printf("\n---debug----\n");
    print(x);
    printf("---debug----\n");
}

int main() {
    freopen("input.in", "r", stdin);
    int n, m;
    scanf("%d%d",&n,&m);

    tree.init(n+1);
    //debug();
    int a, b;
    SplayTreeNode *first, *second, *third;
    REP(i, 1, m) {
        scanf("%d%d",&a,&b);
        tree.split(tree.root, a, first, second);
        tree.split(second, b - a + 1, second, third);
        second->flip = !second->flip;
        if (third != null) {
            tree.merge(third, second);
            tree.merge(first, third);
        } else {
            tree.merge(first, second);
        }
        //debug(third);
        tree.root = first;
        //debug(tree.root);
    }
    print(tree.root);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值