splay(区间)

转载请注明出处,部分内容引自Mychael大神的博客

既然你看到这里,相信你一定了解splay的思想。

splay按照STB的性质进行维护,并将其均摊复杂度降为了log(n)

splay既然被称为序列之王,想必还有其他应用。

对于翻转操作,splay有着极强的灵活性;对于区间划分,线段树的思想也十分优秀

如果按照线段树的方式构造splay...那么一颗区间平衡树就诞生啦!(没错,就这么简单)

Splay的区间操作

我们知道查找树的中序遍历是一个有序的序列。这个时候我们不采用查找树左小右大的规则,而是把它的中序遍历作为区间进行维护。

具体来讲有以下操作:

  1. 建树
  2. 区间操作(翻转、赋值...)
  3. 输出序列

建树

对于区间操作,我们采用线段树的结构进行存储

 

void build(int& x,int l,int r,int fa){
    if(l>=r) return ;
    x=++tot;
    int mid=l+r>>1;
    t[x].val=mid;
    t[x].size=1;
    t[x].ff=fa;
    if(l+1<r){
        build(t[x].ch[0],l,mid,x);
        build(t[x].ch[1],mid+1,r,x);
        t[x].size+=t[t[x].ch[0]].size+t[t[x].ch[1]].size;
    }
}

 

 

区间操作

这时我们想要从树中查找我们所需的区间,如何操作呢?

splay的伸展操作可以在不改变中序遍历的前提下改变树的结构,也就是说我们可以在不改变序列的前提下改变树的形态。

那么这个时候,如果我们要操作区间 [l,r] 我们只需将 l-1 翻转到根节点,将 r+1 翻转到根节点的右儿子

那么这时,r+1 的左子树就是我们所要的区间 [l,r]

 

如何查找这两个节点?

套用splay的第k大节点好了(这里应该是第k个)

至于找到区间后的操作,采用线段树 lazytag 的思想仍然可以将均摊复杂度降低在 logn

 

void rotate(int x){
    int y=t[x].ff,k=get(x);
    if(t[y].ff) t[t[y].ff].ch[get(y)]=x;
    t[x].ff=t[y].ff;
    t[y].ch[k]=t[x].ch[k^1];
    if(t[x].ch[k^1]) t[t[x].ch[k^1]].ff=y;
    t[x].ch[k^1]=y;
    t[y].ff=x;
    update(y);
}
void splay(int x,int goal){
    for(int y=t[x].ff;y=t[x].ff,y!=goal;rotate(x)){
        if(t[y].ff&&lazy[t[y].ff]) push_down(t[y].ff);
        if(lazy[y]) push_down(y);
        if(lazy[x]) push_down(x);
        if(t[y].ff!=goal) rotate(get(y)==get(x)?y:x);
    }
    if(!goal) root=x;
    update(x);
}

 

 

 

输出序列

对应的中序遍历

 

代码

 

#include<bits/stdc++.h>
#define ls(p) p<<1
#define rs(p) p<<1|1
using namespace std;
const int N=3e6+10;
const int INF=1e9+10;
struct splay_tree{
    int val,ch[2],ff,size;
}t[N];
int n,m,tot,root;
int lazy[N];
int get(int x){
    int y=t[x].ff;
    return t[y].ch[1]==x?1:0;
}
void update(int x){
    t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
}
void push_down(int x){
    swap(t[x].ch[0],t[x].ch[1]);
    lazy[t[x].ch[0]]^=1;
    lazy[t[x].ch[1]]^=1;
    lazy[x]^=1;
}
void rotate(int x){
    int y=t[x].ff,k=get(x);
    if(t[y].ff) t[t[y].ff].ch[get(y)]=x;
    t[x].ff=t[y].ff;
    t[y].ch[k]=t[x].ch[k^1];
    if(t[x].ch[k^1]) t[t[x].ch[k^1]].ff=y;
    t[x].ch[k^1]=y;
    t[y].ff=x;
    update(y);
}
void splay(int x,int goal){
    for(int y=t[x].ff;y=t[x].ff,y!=goal;rotate(x)){
        if(t[y].ff&&lazy[t[y].ff]) push_down(t[y].ff);
        if(lazy[y]) push_down(y);
        if(lazy[x]) push_down(x);
        if(t[y].ff!=goal) rotate(get(y)==get(x)?y:x);
    }
    if(!goal) root=x;
    update(x);
}
int kth(int x,int k){
    if(lazy[x]) push_down(x);
    if(k==t[t[x].ch[0]].size+1) return x;
    else if(k>t[t[x].ch[0]].size+1) return kth(t[x].ch[1],k-t[t[x].ch[0]].size-1);
    return kth(t[x].ch[0],k);
}
void change(int l,int r){
    int L=kth(root,l),R=kth(root,r+2);
    splay(L,0),splay(R,root);
    lazy[t[t[root].ch[1]].ch[0]]^=1;
}
void build(int& x,int l,int r,int fa){
    if(l>=r) return ;
    x=++tot;
    int mid=l+r>>1;
    t[x].val=mid;
    t[x].size=1;
    t[x].ff=fa;
    if(l+1<r){
        build(t[x].ch[0],l,mid,x);
        build(t[x].ch[1],mid+1,r,x);
        t[x].size+=t[t[x].ch[0]].size+t[t[x].ch[1]].size;
    }
}
void print(int x){
    if(x){
        if(lazy[x]) push_down(x);
        print(t[x].ch[0]);
        if(t[x].val>=1&&t[x].val<=n) cout<<t[x].val<<' ';
        print(t[x].ch[1]);
    }
}
int main(){
    cin>>n>>m;
    build(root,0,n+2,0);
    while(m--){
        int a,b;
        cin>>a>>b;
        if(a==b) continue;
        change(a,b);
    }
    print(root);
    return 0;
}

 

posted @ 2023-03-30 11:27  青阳buleeyes  阅读(51)  评论(0)    收藏  举报