splay(区间)
转载请注明出处,部分内容引自Mychael大神的博客
既然你看到这里,相信你一定了解splay的思想。
splay按照STB的性质进行维护,并将其均摊复杂度降为了log(n)
splay既然被称为序列之王,想必还有其他应用。
对于翻转操作,splay有着极强的灵活性;对于区间划分,线段树的思想也十分优秀
如果按照线段树的方式构造splay...那么一颗区间平衡树就诞生啦!(没错,就这么简单)
Splay的区间操作
我们知道查找树的中序遍历是一个有序的序列。这个时候我们不采用查找树左小右大的规则,而是把它的中序遍历作为区间进行维护。
具体来讲有以下操作:
- 建树
- 区间操作(翻转、赋值...)
- 输出序列
建树
对于区间操作,我们采用线段树的结构进行存储
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; }

浙公网安备 33010602011771号