【BZOJ3223】文艺平衡树【Splay】

在此之前,我学会了treap,因此学起splay来很轻松

splay又叫伸展树,可以分裂和合并,从而实现区间操作。splay,和treap是不同的。treap是二叉搜索树,对节点权值满足左小右大的特性。splay是由序列信息组成的一颗树,满足一大特性——中序遍历后得到原序列。换句话说,splay中每个节点都对应序列的一个位置坐标。往左子树走,就是这个位置往左;往右子树走则相反。也可以这样理解:splay是一颗bst(二叉搜索树),不过每个节点的权值是位置坐标而已,附带着这个位置上的元素

 

splay有3种基本操作:

  1.build(A):把数组A转化为序列

  2.merge(S1,S2):把序列S1和S2连接在一起,S1在左边,S2在右边。返回新序列

  3.split(S,k):把序列S分成两个连续的子序列,左子序列包含S的前k个元素,其他元素在右子树中

这三种操作可以由伸展操作splay(S,k)实现,即把子树S的第k个元素旋转到根上,详见白书,此处略过...

 

对于这道题,反转操作要这样实现:先截取所要的区间,交换左右子树,并打上标记,从而实现逐层交换

其实很好理解——反转区间后,位置坐标原本靠左的,现在变得靠右了嘛

注意每访问一个点,都要下传标记(类似线段树)

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>

using namespace std;

struct Node{
    Node* ch[2];
    int s,v;
    bool tg;
    
    int cmp(int k) const {
        int d = k - ch[0]->s;
        if(d == 1) return -1;
        return d <= 0 ? 0 : 1;
    }
    void tain() {
        s = ch[0]->s + ch[1]->s + 1;
    }
    void pushdown() {
        if(!tg)return;
        swap(ch[0], ch[1]);
        ch[0]->tg^=1;
        ch[1]->tg^=1;
        tg=0;
    }
    
};

Node* null = new Node();

void rotate(Node* &o,int d) {
    Node* k = o->ch[d^1];
    o->ch[d^1] = k->ch[d]; 
    k->ch[d] = o;
    o->tain(); k->tain();
    o = 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);
    }
}
Node* merge(Node* l,Node* r) {
    splay(l,l->s);
    l->ch[1] = r;
    l->tain();
    return l;
}
typedef pair<Node*,Node*> D;
D split(Node* o,int k) {
    splay(o, k);
    Node* l=o;
    Node* r=o->ch[1];
    o->ch[1] = null;
    l->tain();
    return D(l,r);
}

Node* build(int l,int r)
{
    if(l>r)return null;
    int mid = (l+r)>>1;
    Node* x = new Node();
    x->tg=0; x->v=mid;
    x->ch[0] = build(l,mid-1);
    x->ch[1] = build(mid+1,r);
    x->tain();
    return x;
}

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

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    null->s=0;
    Node* root=build(0,n+1);
    int l,r;
    while(m--) {
        scanf("%d%d",&l,&r);
        D x=split(root,l);
        D y=split(x.second,r-l+1);
        y.first->tg^=1;
        root=merge(x.first,merge(y.first,y.second));
    }
    travel(root);
    for(int i=1;i<n;i++)
        printf("%d ",ans[i]);
    printf("%d\n",ans[n]);
    return 0;
}

 

还有一种数据结构叫无旋treap,和splay的概念是近乎相同的,个人感觉比splay还好写,想学习的同学可以看下这篇博文https://www.cnblogs.com/LadyLex/p/7182631.html

 

posted @ 2018-08-20 15:34  mgnfcnt  阅读(152)  评论(0编辑  收藏  举报