Uva 11922 Splay

Posted 树的种子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Uva 11922 Splay相关的知识,希望对你有一定的参考价值。

Splay(伸展树)实现可分裂与合并的序列

对于BST,除了Treap树之外,还有一种Splay的伸展树,他能快速的分裂与合并。

 

重要的操作是伸展操作,将一个指定的结点 x 旋转到根的过程。

分三种情况,一次单旋,两次同向单旋,两次反向旋转。可以手动模拟一下这个过程。

 

到这里,问题常常是将序列的第 k 个元素旋转到根。

首先,要知道的是伸展树里面v存的是什么,是节点的编号(下标)。这样才能像 Treap实现名次树那样,很方便的找到左边第 k 个元素。

//将序列左数第k个元素选择到根
void splay(Node* &o,int k) {
    o->pushdown();
    int d = o->cmp(k);
    if(d==1) k-=o->ch[0]->s + 1;
    if(d!=-1) {
        Node* 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);
    }
}

 

分裂与合并:

分裂:从序列从左第 k 个元素分裂,就是将序列的 o 的第 K 小元素伸展到根,断开树根与右子节点。

合并:将left部分最大的元素旋转到根,将right作为 left的右子树。(保证right>left所有元素)。

// 合并操作。假定left所有元素小于 right
Node* merge(Node* left,Node* right) {
    splay(left,left->s);
    left->ch[1] = right;
    left->maintain();
    return left;
}
?
//把 o 前 k 个小结点放到left里面,其他放到ritht里面,如果不够right = null
void split(Node* o,int k,Node* &left,Node* &right) {
    splay(o,k);
    left = o;
    right = o->ch[1];
    o->ch[1] = null;
    left->maintain();
}

 

有时,对于序列有反转操作,这时,利用 线段树的 lazy标记,标记某一段是否反转。

对于,数据结构的定义:用一个Node数组,和一个Node 的 root指针,指向这个数组的元素。

 

 

#include <bits/stdc++.h>

using namespace std;

struct Node {
    Node *ch[2];
    int s;
    int flip;
    int v;
    int cmp(int k) const {
        int d = k - ch[0]->s;
        if(d==1) return -1;     //序列第 k 个找到
        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;
        }
    }
};

Node *null = new Node();

// d = 0 左旋
void rotate(Node* &o,int d) {
    Node* k = o->ch[d^1];
    o->ch[d^1] = k->ch[d];
    k->ch[d] = o;
    o->maintain();
    k->maintain();
    o = k;
}


//将序列左数第k个元素选择到根
void splay(Node* &o,int k) {
    o->pushdown();
    int d = o->cmp(k);
    if(d==1) k-=o->ch[0]->s + 1;
    if(d!=-1) {
        Node* 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);
    }
}

// 合并操作。假定left所有元素小于 right
Node* merge(Node* left,Node* right) {
    splay(left,left->s);
    left->ch[1] = right;
    left->maintain();
    return left;
}

//把 o 前 k 个小结点放到left里面,其他放到ritht里面,如果不够right = null
void split(Node* o,int k,Node* &left,Node* &right) {
    splay(o,k);
    left = o;
    right = o->ch[1];
    o->ch[1] = null;
    left->maintain();
}

const int maxn = 1e5+10;
struct SplaySequence {
    int n;
    Node seq[maxn];
    Node *root;

    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);
        for(int i = 0; i < sz; i++)
            printf("%d ",seq[i].v);
        puts("");
    }

};

vector<int> ans;
void print(Node* o) {
    if(o!=null) {
        o->pushdown();
        print(o->ch[0]);
        ans.push_back(o->v);
        print(o->ch[1]);
    }
}

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

SplaySequence ss;

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    ss.init(n+1);

    debug(ss.root);


    for(int i = 0; i < m; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        Node* left,*mid,*right,*o;
        split(ss.root,a,left,o);
        split(o,b-a+1,mid,right);
        mid->flip ^=1;
        ss.root = merge(merge(left,right),mid);
    }

    print(ss.root);
    for(int i = 1; i < (int)ans.size(); i++)
        printf("%d\n",ans[i]-1);

    return 0;
}

 

以上是关于Uva 11922 Splay的主要内容,如果未能解决你的问题,请参考以下文章

UVa 11922 & splay的合并与分裂

UVA 11922 Permutation Transformer —— splay伸展树

Uva 11922 Permutation Transformer

Splay

UVA11922 Permutation Transformer

UVA - 11922 区间反转+拼接 可持久化Treap