Splay这东西神难打……什么都没动板子敲上就直逼200行了,而且非常难记(仿佛是模板长的必然结果)。但是为什么还要学呢?据说是因为它可以实现区间操作。但是自从我得知无旋Treap也能做到这些,默默对比了一下代码长度之后分分钟抛弃Splay啊= =。
和Treap用随机值和左右旋维护平衡不同的,Splay用它的核心操作Splay来维护平衡。所谓的Splay操作可以把任何一个节点旋转到它的一个祖先节点,而旋转分单旋和双旋,双旋需要对比它与父亲是否在各自父亲的同侧。然后每次需要打标记移区间删树之类的,它居然要把目标区间的两端分别移到根和根的儿子……极其麻烦啊这个东西。放几道例题,再体会吧。见到Splay,才知Treap好。
普通平衡树[Tyvj 1728]
时间限制:1 s 内存限制:128 MB
【题目描述】
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
【输入格式】
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
【输出格式】
对于操作3,4,5,6每行输出一个数,表示对应答案
【样例输入】
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
【样例输出】
106465
84185
492737
【提示】
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int sj=1000010; int ch[sj][2],f[sj],size[sj],cnt[sj],key[sj]; int sz,root; inline void clear(int x) { ch[x][0]=ch[x][1]=f[x]=size[x]=cnt[x]=key[x]=0; } inline bool get(int x) { return ch[f[x]][1]==x; } inline void update(int x) { if(x) { size[x]=cnt[x]; if(ch[x][0]) size[x]+=size[ch[x][0]]; if(ch[x][1]) size[x]+=size[ch[x][1]]; } } inline void rotate(int x) { int old=f[x],oldf=f[old],whichx=get(x); ch[old][whichx]=ch[x][whichx^1]; f[ch[old][whichx]]=old; ch[x][whichx^1]=old; f[old]=x; f[x]=oldf; if(oldf) ch[oldf][ch[oldf][1]==old]=x; update(old); update(x); } inline void splay(int x) { for(int fa;fa=f[x];rotate(x)) if(f[fa]) rotate(get(x)==get(fa)?fa:x); root=x; } inline void insert(int x) { if(root==0) { sz++; ch[sz][0]=ch[sz][1]=f[sz]=0; root=sz; size[sz]=cnt[sz]=1; key[sz]=x; return; } int now=root,fa=0; while(1) { if(x==key[now]) { cnt[now]++; update(now); update(fa); splay(now); break; } fa=now; now=ch[now][key[now]<x]; if(now==0) { sz++; ch[sz][0]=ch[sz][1]=0; f[sz]=fa; size[sz]=cnt[sz]=1; ch[fa][key[fa]<x]=sz; key[sz]=x; update(fa); splay(sz); break; } } } inline int find(int x) { int now=root,ans=0; while(1) { if(x<key[now]) now=ch[now][0]; else { ans+=(ch[now][0]?size[ch[now][0]]:0); if(x==key[now]) { splay(now); return ans+1; } ans+=cnt[now]; now=ch[now][1]; } } } inline int findx(int x) { int now=root; while(1) { if(ch[now][0]&&x<=size[ch[now][0]]) now=ch[now][0]; else { int temp=(ch[now][0]?size[ch[now][0]]:0)+cnt[now]; if(x<=temp) return key[now]; x-=temp; now=ch[now][1]; } } } inline int pre() { int now=ch[root][0]; while(ch[now][1]) now=ch[now][1]; return now; } inline int next() { int now=ch[root][1]; while(ch[now][0]) now=ch[now][0]; return now; } inline void del(int x) { int whatever=find(x); if(cnt[root]>1) { cnt[root]--; update(root); return; } if(!ch[root][0]&&!ch[root][1]) { clear(root); root=0; return; } if(!ch[root][0]) { int oldroot=root; root=ch[root][1]; f[root]=0; clear(oldroot); return; } else if(!ch[root][1]) { int oldroot=root; root=ch[root][0]; f[root]=0; clear(oldroot); return; } int leftbig=pre(),oldroot=root; splay(leftbig); ch[root][1]=ch[oldroot][1]; f[ch[oldroot][1]]=root; clear(oldroot); update(root); } int main() { int n,opt,x; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&opt,&x); if(opt==1) insert(x); if(opt==2) del(x); if(opt==3) printf("%d\n",find(x)); if(opt==4) printf("%d\n",findx(x)); if(opt==5) { insert(x); printf("%d\n",key[pre()]); del(x); } if(opt==6) { insert(x); printf("%d\n",key[next()]); del(x); } } return 0; }
[HZOI 2016][Tyvj 1729]文艺平衡树
时间限制:1 s 内存限制:128 MB
【题目描述】
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
【输入格式】
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
【输出格式】
输出一行n个数字,表示原始序列经过m次变换后的结果
【样例输入】
5 3
1 3
1 3
1 4
【样例输出】
4 3 2 1 5
【数据范围】
N,M<=100000
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct node { int siz,sum,flag; node *ch[2],*fa; void pushdown(node *nd) { if(flag) { swap(ch[0],ch[1]); if(ch[0]!=nd) ch[0]->flag^=1; if(ch[1]!=nd) ch[1]->flag^=1; flag=0; } } void update() { siz=ch[0]->siz+ch[1]->siz+1; } }c[4000010],*tail=c,*root,*null; int n,m,a1,a2; void init() { null=++tail; null->siz=0; null->ch[0]=null->ch[1]=null; null->sum=null->flag=0; } node* newnode(node *fa) { node *nd=++tail; nd->fa=fa; nd->siz=1; nd->ch[1]=nd->ch[0]=null; nd->flag=0; return nd; } void rot(node*& x,int d) { node* y=x->fa; y->ch[!d]=x->ch[d]; if(x->ch[d]!=null) x->ch[d]->fa=y; x->fa=y->fa; if(y->fa!=null) (y==y->fa->ch[0])?y->fa->ch[0]=x:y->fa->ch[1]=x; x->ch[d]=y; y->fa=x; x->update(); y->update(); } node *build(node *fa,int lf,int rg) { if(lf>rg) return null; node *nd=newnode(fa); if(lf==rg) { nd->sum=lf; return nd; } int mid=(lf+rg)>>1; nd->sum=mid; nd->ch[0]=build(nd,lf,mid-1); nd->ch[1]=build(nd,mid+1,rg); nd->update(); return nd; } void splay(node *nd,node *tar) { while(nd->fa!=tar) { node *ne=nd->fa; if(nd==ne->ch[0]) { if(ne->fa!=tar&&ne==ne->fa->ch[0]) rot(ne,1); rot(nd,1); } else { if(ne->fa!=tar&&ne==ne->fa->ch[1]) rot(ne,0); rot(nd,0); } } if(tar==null) root=nd; } node *kth(node *nd,int k) { nd->pushdown(null); if(nd->ch[0]->siz+1==k) return nd; if(nd->ch[0]->siz+1>k) return kth(nd->ch[0],k); else return kth(nd->ch[1],k-nd->ch[0]->siz-1); } void rev(int l,int r) { node *x=kth(root,l); node *y=kth(root,r+2); splay(x,null); splay(y,root); y->ch[0]->flag^=1; } void dfs(node *nd) { if(nd==null) return; nd->pushdown(null); dfs(nd->ch[0]); if(nd->sum>=1&&nd->sum<=n) printf("%d ",nd->sum); dfs(nd->ch[1]); } int main() { scanf("%d%d",&n,&m); init(); root=build(null,0,n+1); for(int i=1;i<=m;i++) { scanf("%d%d",&a1,&a2); rev(a1,a2); } dfs(root); return 0; }
Description
Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1, A2, ... An}. Then the host performs a series of operations and queries on the sequence which consists:
- ADD x y D: Add D to each number in sub-sequence {Ax ... Ay}. For example, performing "ADD 2 4 1" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5, 5}
- REVERSE x y: reverse the sub-sequence {Ax ... Ay}. For example, performing "REVERSE 2 4" on {1, 2, 3, 4, 5} results in {1, 4, 3, 2, 5}
- REVOLVE x y T: rotate sub-sequence {Ax ... Ay} T times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
- INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5}
- DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
- MIN x y: query the participant what is the minimum number in sub-sequence {Ax ... Ay}. For example, the correct answer to "MIN 2 4" on {1, 2, 3, 4, 5} is 2
To make the show more interesting, the participant is granted a chance to turn to someone else that means when Jackson feels difficult in answering a query he may call you for help. You task is to watch the TV show and write a program giving the correct answer to each query in order to assist Jackson whenever he calls.
Input
The first line contains n (n ≤ 100000).
The following n lines describe the sequence.
Then follows M (M ≤ 100000), the numbers of operations and queries.
The following M lines describe the operations and queries.
Output
For each "MIN" query, output the correct answer.
Sample Input
5
1
2
3
4
5
2
ADD 2 4 1
MIN 4 5
Sample Output
5
Source
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define ky ch[ch[root][1]][0] using namespace std; const int sj=200010; const int wq=0x3f3f3f3f; int n,a[sj],a1,a2,a3,m,rev[sj],mi[sj],add[sj],s[sj]; char ss[10]; int pre[sj],ch[sj][2],root,tot1,size[sj],key[sj],tot2; void newnode(int &r,int father,int k) { if(tot2) r=s[tot2--]; else r=++tot1; pre[r]=father; ch[r][0]=ch[r][1]=0; key[r]=k; mi[r]=k; rev[r]=add[r]=0; size[r]=1; } void update_rev(int r) { if(!r) return; swap(ch[r][0],ch[r][1]); rev[r]^=1; } void update_add(int r,int d) { if(!r) return; mi[r]+=d; key[r]+=d; add[r]+=d; } void push_up(int r) { size[r]=size[ch[r][0]]+size[ch[r][1]]+1; mi[r]=min(key[r],min(mi[ch[r][0]],mi[ch[r][1]])); } void push_down(int r) { if(rev[r]) { update_rev(ch[r][0]); update_rev(ch[r][1]); rev[r]=0; } if(add[r]) { update_add(ch[r][0],add[r]); update_add(ch[r][1],add[r]); add[r]=0; } } void build(int &x,int l,int r,int father) { if(l>r) return; int mid=(l+r)>>1; newnode(x,father,a[mid]); build(ch[x][0],l,mid-1,x); build(ch[x][1],mid+1,r,x); push_up(x); } void rotate(int x,int kind) { int y=pre[x]; push_down(y); push_down(x); ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; push_up(y); } void splay(int r,int goal) { push_down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal) { push_down(pre[r]); push_down(r); rotate(r,ch[pre[r]][0]==r); } else { push_down(pre[pre[r]]); push_down(pre[r]); push_down(r); int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r) { rotate(r,!kind); rotate(r,kind); } else { rotate(y,kind); rotate(r,kind); } } } push_up(r); if(goal==0) root=r; } int get_kth(int r,int k) { push_down(r); int t=size[ch[r][0]]+1; if(t==k) return r; if(t>k) return get_kth(ch[r][0],k); else return get_kth(ch[r][1],k-t); } void Add(int x,int y,int d) { splay(get_kth(root,x),0); splay(get_kth(root,y+2),root); update_add(ky,d); push_up(ch[root][1]); push_up(root); } void reverse(int x,int y) { splay(get_kth(root,x),0); splay(get_kth(root,y+2),root); update_rev(ky); push_up(ch[root][1]); push_up(root); } void revolve(int x,int y,int t) { int len=y-x+1; t=(t%len+len)%len; splay(get_kth(root,y-t+1),0); splay(get_kth(root,y+2),root); int tmp=ky; ky=0; push_up(ch[root][0]); push_up(root); splay(get_kth(root,x),0); splay(get_kth(root,x+1),root); ky=tmp; pre[tmp]=ch[root][1]; push_up(ch[root][1]); push_up(root); } void insert(int x,int p) { splay(get_kth(root,x+1),0); splay(get_kth(root,x+2),root); newnode(ky,ch[root][1],p); push_up(ch[root][1]); push_up(root); } void erase(int r) { if(!r) return; s[++tot2]=r; erase(ch[r][0]); erase(ch[r][1]); } void Delete(int x) { splay(get_kth(root,x),0); splay(get_kth(root,x+2),root); erase(ky); pre[ky]=0; ky=0; push_up(ch[root][1]); push_up(root); } int Min(int x,int y) { splay(get_kth(root,x),0); splay(get_kth(root,y+2),root); return mi[ky]; } int main() { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); mi[root]=wq; newnode(root,0,-1); newnode(ch[root][1],root,-1); build(ky,0,n-1,ch[root][1]); push_up(ch[root][1]); push_up(root); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%s%d",ss,&a1); if(ss[0]=='A') scanf("%d%d",&a2,&a3),Add(a1,a2,a3); if(ss[0]=='R') { scanf("%d",&a2); if(ss[3]=='E') reverse(a1,a2); if(ss[3]=='O') scanf("%d",&a3),revolve(a1,a2,a3); } if(ss[0]=='I') scanf("%d",&a2),insert(a1,a2); if(ss[0]=='D') Delete(a1); if(ss[0]=='M') scanf("%d",&a2),printf("%d\n",Min(a1,a2)); } return 0; }
为天地立心,为生民请命,为往圣继绝学,为万世开太平。