splay
\(splay\)
基本操作
简述
Splay通过splay操作(将子节点旋转至根),来维护树高
由于要旋到根,所以要维护节点的父亲,如下
struct Splay{
int fa,sz,ct,vl,s[2];
}tr[N];
il void clear(cs int x){
tr[x].vl=tr[x].sz=tr[x].ct=0;
tr[x].s[0]=tr[x].s[1]=tr[x].fa=0;
}
il bool son(cs int x){return x==tr[tr[x].fa].s[1];}
il void updsz(cs int x){
tr[x].sz=tr[x].ct+tr[tr[x].s[0]].sz+tr[tr[x].s[1]].sz;
}
\(rotate\)
\(rotate\)操作和\(treap\)的差不多
il void rotate(cs int x){
int f=tr[x].fa,ff=tr[f].fa;
if(!f) return;bool l=son(x);//l就是旋转方向
tr[f].s[l]=tr[x].s[l^1];
if(tr[x].s[l^1]) tr[tr[x].s[l^1]].fa=f;
tr[x].s[l^1]=f;//不要忘记更新子节点它爹
if(ff) tr[ff].s[son(f)]=x;
tr[x].fa=ff,tr[f].fa=x,updsz(f),updsz(x);
}
\(splay\)
\(splay\)操作可以将节点\(x\)旋转到指定位置(这里是根)
为维护树高,需要双旋(有左旋也有右旋)
具体来说
如果\(x\)和它的父节点同为左儿子或右儿子(共线),先旋转父节点,再旋转\(x\)
如果\(x\)和它的父节点分别为左、右儿子(不共线),直接旋转\(x\)即可
在链很长的时候,一次\(splay\)操作之后,这条链的深度会减半。(可以自己画图看看)
il void splay(cs int x){
for(ri int f;f=tr[x].fa;rotate(x)){
if(tr[f].fa) rotate(son(f)^son(x)?x:f);
}
rt=x;
}
基本上每次操作之后都要\(splay\)以维护树高 (所以常数是真的大)
\(insert\)
to be continue~
il void insert(cs int x){
ri int t=rt,f=0;
while(t&&tr[t].vl!=x) f=t,t=tr[f].s[x>tr[f].vl];
if(t) tr[t].ct++,splay(t);
else{
t=++tot,tr[t].vl=x,tr[t].ct++,tr[t].fa=f;
tr[f].s[x>tr[f].vl]=t,splay(t);
}
}
查询数\(x\)的排名
to be continue~
il int rnk(cs int x){
ri int t=rt,f=0,rk=1;
while(t){
if(tr[t].vl>x) t=tr[t].s[0];
else{
rk+=tr[tr[t].s[0]].sz;
if(tr[t].vl==x) break;
rk+=tr[t].ct,t=tr[t].s[1];
}
}
if(t) splay(t);
return rk;
}
查询排名为\(x\)的数
to be continue~
il int xth(int x){
ri int t=rt;
while(t){
if(tr[tr[t].s[0]].sz>=x) t=tr[t].s[0];
else{
x-=tr[tr[t].s[0]].sz+tr[t].ct;
if(x<=0) break;t=tr[t].s[1];
}
}
if(t) splay(t);
return tr[t].vl;
}
查询数\(x\)的前驱&后继
to be continue~
前驱
il int per(){
int t=tr[rt].s[0];
if(!t) return 0;
while(tr[t].s[1]) t=tr[t].s[1];
return splay(t),t;
}
il int get_per(cs int x){
insert(x);int k=per();
del(x);return tr[k].vl;
}
后继
il int nxt(){
int t=tr[rt].s[1];
if(!t) return 0;
while(tr[t].s[0]) t=tr[t].s[0];
return splay(t),t;
}
il int get_nxt(cs int x){
insert(x);int k=nxt();
del(x);return tr[k].vl;
}
发现它们长得很像,可以写一起 (绝对不是偷懒)
il int near(cs bool l){
int t=tr[rt].s[l^1];
if(!t) return 0;
while(tr[t].s[l]) t=tr[t].s[l];
return splay(t),t;
}
il int sol(cs int x,cs bool l){
insert(x);int k=near(l);
del(x);return tr[k].vl;
}
(\(ps:delete\)操作见下)
\(delete\)
to be continue~
il void del(cs int x){
rnk(x);int t=rt;
if(tr[rt].vl!=x) return;
if(tr[t].ct>1) tr[t].ct--,updsz(t);
else if(!tr[t].s[0]&&!tr[t].s[1]) clear(t),rt=0;
else if(!tr[t].s[0]) rt=tr[t].s[1],clear(t),tr[rt].fa=0;
else if(!tr[t].s[1]) rt=tr[t].s[0],clear(t),tr[rt].fa=0;
else{
near(1);tr[rt].fa=0,tr[rt].s[1]=tr[t].s[1];
tr[tr[t].s[1]].fa=rt,clear(t),updsz(rt);
}
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
namespace _314159_x{
using namespace std;
il int rd(){
ri int x=0,f=1;ri char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return x*f;
}
short tl=0,wr[20];
il void wt(int x){
if(x<0) x=-x,putchar('-');
do wr[++tl]=x%10,x/=10; while(x);
while(tl) putchar(wr[tl--]+48);
putchar('\n'),tl=0;
}
cs int N=2e5+5;
int rt,tot;
struct Splay{
int fa,sz,ct,vl,s[2];
}tr[N];
il void clear(cs int x){
tr[x].vl=tr[x].sz=tr[x].ct=0;
tr[x].s[0]=tr[x].s[1]=tr[x].fa=0;
}
il bool son(cs int x){return x==tr[tr[x].fa].s[1];}
il void updsz(cs int x){
tr[x].sz=tr[x].ct+tr[tr[x].s[0]].sz+tr[tr[x].s[1]].sz;
}
il void rotate(cs int x){
int f=tr[x].fa,ff=tr[f].fa;
if(!f) return;bool l=son(x);
tr[f].s[l]=tr[x].s[l^1];
if(tr[x].s[l^1]) tr[tr[x].s[l^1]].fa=f;
tr[x].s[l^1]=f;//!!!!!!!!!!
if(ff) tr[ff].s[son(f)]=x;
tr[x].fa=ff,tr[f].fa=x,updsz(f),updsz(x);
}
il void splay(cs int x){
for(ri int f;f=tr[x].fa;rotate(x)){
if(tr[f].fa) rotate(son(f)^son(x)?x:f);
}
rt=x;
}
il void insert(cs int x){
ri int t=rt,f=0;
while(t&&tr[t].vl!=x) f=t,t=tr[f].s[x>tr[f].vl];
if(t) tr[t].ct++,splay(t);
else{
t=++tot,tr[t].vl=x,tr[t].ct++,tr[t].fa=f;
tr[f].s[x>tr[f].vl]=t,splay(t);
}
}
il int rnk(cs int x){
ri int t=rt,f=0,rk=1;
while(t){
if(tr[t].vl>x) t=tr[t].s[0];
else{
rk+=tr[tr[t].s[0]].sz;
if(tr[t].vl==x) break;
rk+=tr[t].ct,t=tr[t].s[1];
}
}
if(t) splay(t);
return rk;
}
il int xth(int x){
ri int t=rt;
while(t){
if(tr[tr[t].s[0]].sz>=x) t=tr[t].s[0];
else{
x-=tr[tr[t].s[0]].sz+tr[t].ct;
if(x<=0) break;t=tr[t].s[1];
}
}
if(t) splay(t);
return tr[t].vl;
}
il int near(cs bool l){
int t=tr[rt].s[l^1];
if(!t) return 0;
while(tr[t].s[l]) t=tr[t].s[l];
return splay(t),t;
}
il void del(cs int x){
rnk(x);int t=rt;
if(tr[rt].vl!=x) return;
if(tr[t].ct>1) tr[t].ct--,updsz(t);
else if(!tr[t].s[0]&&!tr[t].s[1]) clear(t),rt=0;
else if(!tr[t].s[0]) rt=tr[t].s[1],clear(t),tr[rt].fa=0;
else if(!tr[t].s[1]) rt=tr[t].s[0],clear(t),tr[rt].fa=0;
else{
near(1);tr[rt].fa=0,tr[rt].s[1]=tr[t].s[1];
tr[tr[t].s[1]].fa=rt,clear(t),updsz(rt);
}
}
il int sol(cs int x,cs bool l){
insert(x);int k=near(l);
del(x);return tr[k].vl;
}
}
using namespace _314159_x;
int main(){
for(ri int i=rd(),op,x;i;--i){
op=rd(),x=rd();
if(op==1) insert(x);
if(op==2) del(x);
if(op==3) wt(rnk(x));
if(op==4) wt(xth(x));
if(op==5) wt(sol(x,1));
if(op==6) wt(sol(x,0));
}
return 0;
}
区间翻转
建树时插入一个极大值和一个极小值,节点权值表示位置
对于修改区间\([l,r]\):
- 查找权值为\(l\)与\(r+2\)的两个节点(其实就是\(siz\)啦)
- 将前者旋转到根,后者旋转到根的左儿子上
- 要修改的区间就是根的右孩子的左子树,直接打标记即可
il void rev(int ql,int qr){
int l=kth(ql),r=kth(qr+2);
spl(l,0),spl(r,l);
t[t[t[rot].s[1]].s[0]].lz^=1;
return;
}
因为是旋转到指定位置,所以\(Splay\)有一点改动
il void spl(int x,int gl){
for(ri int i;(i=t[x].f)!=gl;rtt(x)){
if(t[i].f!=gl) (son(i)==son(x))?rtt(i):rtt(x);
}
if(!gl) rot=x;
return;
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
cs int N=1e5+5,inf=1e9+7;
int n,m,arr[N];
namespace x314{
int rot,id;
struct qwq{
int s[2],lz,sz,vl,f;
}t[N];
il void upd(int rt){
t[rt].sz=t[t[rt].s[0]].sz+t[t[rt].s[1]].sz+1;
return;
}
il void pdl(int rt){
if(t[rt].lz){
if(t[rt].s[0]) t[t[rt].s[0]].lz^=1;
if(t[rt].s[1]) t[t[rt].s[1]].lz^=1;
swap(t[rt].s[0],t[rt].s[1]);
t[rt].lz^=1;
}
return;
}
il bool son(int rt){
return (rt==t[t[rt].f].s[1]);
}
il void rtt(int rt){
int o=t[rt].f,o2=t[o].f;
if(!o) return;
bool d=son(rt);
t[rt].f=o2;
if(o2) t[o2].s[son(o)]=rt;
t[o].s[d]=t[rt].s[d^1];
if(t[rt].s[d^1]) t[t[rt].s[d^1]].f=o;
t[rt].s[d^1]=o,t[o].f=rt;
return upd(o),upd(rt);
}
il void spl(int x,int gl){
for(ri int i;(i=t[x].f)!=gl;rtt(x)){
if(t[i].f!=gl) (son(i)==son(x))?rtt(i):rtt(x);
}
if(!gl) rot=x;
return;
}
il int kth(int k){
ri int i=rot;
while(i){
pdl(i);//!!!!!!
if(k<=t[t[i].s[0]].sz) i=t[i].s[0];
else{
k-=t[t[i].s[0]].sz+1;
if(k<=0) return i;
i=t[i].s[1];
}
}
return i;
}
il int bui(int l,int r,int fa){
if(l>r) return 0;
int mid=(l+r)>>1;
int i=++id;//!!!!!!!!!
t[i].vl=mid,t[i].f=fa;
t[i].sz=1,t[i].lz=0;
t[i].s[0]=bui(l,mid-1,i);
t[i].s[1]=bui(mid+1,r,i);
return upd(i),i;
}
il void rev(int ql,int qr){
int l=kth(ql),r=kth(qr+2);
spl(l,0),spl(r,l);
t[t[t[rot].s[1]].s[0]].lz^=1;
return;
}
il void prt(int i){
pdl(i);
if(t[i].s[0]) prt(t[i].s[0]);
if(t[i].vl&&t[i].vl<=n) cout<<t[i].vl<<' ';
if(t[i].s[1]) prt(t[i].s[1]);
return;
}
} using namespace x314;
signed main(){
cin>>n>>m;
rot=bui(0,n+1,0);
for(ri int i=1,l,r;i<=m;++i){
cin>>l>>r,rev(l,r);
}
prt(rot);
return 0;
}
I went to the woods because I wanted to live deliberately, I wanted to live deep and suck out all the marrow of life, and not when I had come to die, discover that I had not live.