ybtAu「高级数据结构」第3章 平衡树Splay
Warning:本章大部分题目用块状链表而非 Splay 实现,如果想学 Splay 不要看这篇。
A. 【例题1】波动值之和
不难发现,使 \(|a_i-a_j|\) 最小的 \(a_j\) 一定是 \(a_i\) 在前面的数中的前驱或后继。
如果这个数已经出现过了,那么答案为 \(0\),且无需再插入。
单点插入,求前驱后继,可以使用块链。
注意有负数。
#include <iostream>
#include <vector>
#include <algorithm>
int n,ans;
bool ex[2000005];
namespace Rope
{
const int L=500;
struct Node
{
Node *pre,*nxt;
std::vector<int> vc;
Node() {pre=nxt=nullptr;}
void ins(int x) {vc.push_back(x);}
int siz() {return vc.size();}
int fi() {return vc[0];}
int la() {return vc[vc.size()-1];}
std::vector<int>::iterator bg() {return vc.begin();}
std::vector<int>::iterator ed() {return vc.end();}
std::vector<int>::iterator lower(int x) {return std::lower_bound(vc.begin(),vc.end(),x);}
std::vector<int>::iterator upper(int x) {return std::upper_bound(vc.begin(),vc.end(),x);}
} *hed,pool[1005],*tal;
void split(Node *x)
{
Node *y=new Node();
y->nxt=x->nxt,x->nxt=y,y->pre=x;
if(y->nxt) y->nxt->pre=y;
for(int i=L;i<x->siz();i++) y->ins((x->vc)[i]);
(x->vc).erase(x->bg()+L,x->ed());
}
Node* get(int x)
{
for(Node *i=hed;i;i=i->nxt) if(i->la()>=x) return i;
return nullptr;
}
void ins(int x)
{
Node *p=get(x);
(p->vc).emplace(p->lower(x),x);
if(p->siz()>=2*L) split(p);
}
int qpre(int x)
{
Node *p=get(x);
if(p->fi()>=x) return p->pre->la();
return *(p->lower(x)-1);
}
int qnxt(int x)
{
Node *p=get(x);
return *(p->upper(x));
}
void init() {hed=new Node(),hed->ins(-1e9),hed->ins(1e9);}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n;
Rope::init();
for(int i=1,x;i<=n;i++)
{
std::cin>>x;
if(ex[x+1000000]) continue;
if(i>1) ans+=std::min(x-Rope::qpre(x),Rope::qnxt(x)-x);
else ans+=x;
ex[x+1000000]=1,Rope::ins(x);
}
std::cout<<ans;
}
B. 【例题2】文艺平衡树
比起文艺 FHQ,文艺块链还是有点难写的,因为需要处理许多细节。
#include <iostream>
#include <algorithm>
#include <vector>
int n,m;
namespace Rope
{
const int L=500;
struct Node
{
Node *pre,*nxt;
bool tg;
//int id;
std::vector<int> vc;
Node() {tg=0,pre=nxt=nullptr;}
void ins(int x) {vc.push_back(x);}
int siz() {return vc.size();}
void mt() {tg^=1;}
void rev()
{
if(!tg) return;
for(int i=0,j=vc.size()-1;i<j;i++,j--) std::swap(vc[i],vc[j]);
tg=0;
}
} *hed,*tal,pool[50005];
//int tmpid;
void split(Node *x)
{
Node *y=new Node();
y->nxt=x->nxt,x->nxt=y,y->pre=x;
if(y->nxt) y->nxt->pre=y;
for(int i=L;i<x->siz();i++) y->ins((x->vc)[i]);
(x->vc).erase((x->vc).begin()+L,(x->vc).end());
}
void build()
{
hed=new Node();//,hed->id=++tmpid;
Node *t=hed;
for(int i=1;i<=n;i++)
{
t->ins(i);
if(t->siz()>L)
{
Node *u=new Node();
//u->id=++tmpid;
t->nxt=u,u->pre=t,t=u;
}
}
}
void rev(int l,int r)
{
l--,r--;
Node *lb=hed,*rb=hed;
while(l>=lb->siz()&&lb) l-=lb->siz(),lb=lb->nxt;
while(r>=rb->siz()&&rb) r-=rb->siz(),rb=rb->nxt;
if(lb==rb)
{
lb->rev();
for(int i=l,j=r;i<j;i++,j--) std::swap((lb->vc)[i],(lb->vc)[j]);
return;
}
for(Node *i=lb->nxt;i!=rb;i=i->pre) i->mt(),std::swap(i->pre,i->nxt);
lb->rev(),rb->rev();
if(lb->nxt!=rb)
{
rb->pre->pre=lb,lb->nxt->nxt=rb;
std::swap(lb->nxt,rb->pre);
}
std::vector<int> tmp;
for(int i=lb->siz()-1;i>=l;i--) tmp.push_back((lb->vc)[i]);
(lb->vc).erase((lb->vc).begin()+l,(lb->vc).end());
for(int i=r;i>=0;i--) lb->ins((rb->vc)[i]);
for(int i=r+1;i<rb->siz();i++) tmp.push_back((rb->vc)[i]);
rb->vc=tmp;
if(lb->siz()>2*L) split(lb);
if(rb->siz()>2*L) split(rb);
}
void print()
{
for(Node *i=hed;i;i=i->nxt)
{
i->rev();
for(auto j:(i->vc)) std::cout<<j<<' ';
}
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
Rope::build();
for(int i=1,l,r;i<=m;i++) std::cin>>l>>r,Rope::rev(l,r);
Rope::print();
}
C. 维护集合
将集合所有数加 \(k\),相当于之后加入的数都要减 \(k\),并且 \(Minv\) 也要减 \(k\)。所有数减 \(k\) 则相反,而且需要区间删除。
#include <iostream>
#include <algorithm>
#include <vector>
int m,K,cnt;
namespace Rope
{
const int L=500;
int idx,siz;
struct Node
{
Node *pre,*nxt;
std::vector<int> vc;
Node() {pre=nxt=nullptr;}
void ins(int x) {vc.push_back(x);}
int siz() {return vc.size();}
std::vector<int>::iterator lower(int x) {return std::lower_bound(vc.begin(),vc.end(),x);}
} *hed,pool[2005];
void split(Node *x)
{
Node *y=&pool[++idx];
y->nxt=x->nxt,x->nxt=y,y->pre=x;
if(y->nxt) y->nxt->pre=y;
for(int i=L;i<x->siz();i++) y->ins((x->vc)[i]);
(x->vc).erase((x->vc).begin()+L,(x->vc).end());
}
Node* get(int x)
{
for(Node *i=hed;i;i=i->nxt) if(!i->siz()||(i->vc)[i->siz()-1]>=x) return i;
return nullptr;
}
void ins(int x)
{
siz++;
Node *p=get(x);
(p->vc).emplace(p->lower(x),x);
if(p->siz()>=2*L) split(p);
}
void del()
{
while(hed&&(hed->vc)[hed->siz()-1]<K) cnt+=hed->siz(),hed=hed->nxt;
std::vector<int> tmp;
cnt+=hed->siz();
for(int i=0;i<hed->siz();i++) if((hed->vc)[i]>=K) tmp.push_back((hed->vc)[i]),cnt--;
hed->vc=tmp;
}
int qnum(int x)
{
x=siz-cnt-x;
if(x<0) return -1;
for(Node *i=hed;i;i=i->nxt)
{
if(x>=i->siz()) x-=i->siz();
else return (i->vc)[x];
}
return -1;
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(),std::cout.tie(0);
std::cin>>m>>K;
int lvl=0;
Rope::hed=&Rope::pool[++Rope::idx];
Rope::hed->ins(1e9);
for(int i=1,x;i<=m;i++)
{
char op;
std::cin>>op>>x;
if(op=='I') if(x-lvl>=K) Rope::ins(x-lvl);
if(op=='A') lvl+=x,K-=x,Rope::del();
if(op=='S') lvl-=x,K+=x,Rope::del();
if(op=='F')
{
int r=Rope::qnum(x);
if(r!=-1&&r<1e9) std::cout<<r+lvl<<'\n';
else std::cout<<"-1\n";
}
}
std::cout<<cnt<<'\n';
}
D. 普通平衡树
板子。
#include <iostream>
#include <algorithm>
#include <vector>
int m;
namespace Rope
{
const int L=500;
int idx;
struct Node
{
Node *pre,*nxt;
std::vector<int> vc;
Node() {pre=nxt=nullptr;}
void ins(int x) {vc.push_back(x);}
int siz() {return vc.size();}
int back() {return vc[vc.size()-1];}
std::vector<int>::iterator lower(int x) {return std::lower_bound(vc.begin(),vc.end(),x);}
std::vector<int>::iterator upper(int x) {return std::upper_bound(vc.begin(),vc.end(),x);}
} *hed,pool[2005];
void init() {hed=&pool[++idx],hed->ins(1e9);}
void split(Node *x)
{
Node *y=&pool[++idx];
y->nxt=x->nxt,x->nxt=y,y->pre=x;
if(y->nxt) y->nxt->pre=y;
for(int i=L;i<x->siz();i++) y->ins((x->vc)[i]);
(x->vc).erase((x->vc).begin()+L,(x->vc).end());
}
Node* get(int x)
{
for(Node *i=hed;i;i=i->nxt) if(i->back()>=x) return i;
return nullptr;
}
void ins(int x)
{
Node *p=get(x);
(p->vc).emplace(p->lower(x),x);
if(p->siz()>=2*L) split(p);
}
void del(int x)
{
Node *p=get(x);
(p->vc).erase(p->lower(x));
if(p->siz()==0)
{
if(p==hed) hed=p->nxt,hed->pre=nullptr;
if(p->pre) p->pre->nxt=p->nxt;
if(p->nxt) p->nxt->pre=p->pre;
p->pre=p->nxt=nullptr;
}
}
int qrk(int x)
{
int r=1;
for(Node *i=hed;i;i=i->nxt)
{
if(x>i->back()) r+=i->siz();
else return r+(i->lower(x)-(i->vc).begin());
}
return r;
}
int qnum(int x)
{
for(Node *i=hed;i;i=i->nxt)
{
if(x>i->siz()) x-=i->siz();
else return (i->vc)[x-1];
}
return -1;
}
int qpre(int x)
{
Node *p=get(x);
if((p->vc)[0]>=x) return p->pre->back();
return *(p->lower(x)-1);
}
int qnxt(int x)
{
Node *p=get(x);
while(p->back()==x) p=p->nxt;
return *(p->upper(x));
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>m;
Rope::init();
for(int i=1,op,x;i<=m;i++)
{
std::cin>>op>>x;
if(op==1) Rope::ins(x);
if(op==2) Rope::del(x);
if(op==3) std::cout<<Rope::qrk(x)<<'\n';
if(op==4) std::cout<<Rope::qnum(x)<<'\n';
if(op==5) std::cout<<Rope::qpre(x)<<'\n';
if(op==6) std::cout<<Rope::qnxt(x)<<'\n';
}
}
E. 维护书架
把前三个操作拆成删除和插入两个操作即可。注意如果一个块删空了要把这个块也删了。
#include <iostream>
#include <algorithm>
#include <vector>
#define N 80005
int n,m,a[N],id[N];
namespace Rope
{
const int L=500;
int idx;
struct Node
{
Node *pre,*nxt;
int id;
std::vector<int> vc;
Node() {pre=nxt=nullptr;}
void ins(int x) {vc.push_back(x);}
int siz() {return vc.size();}
} *hed,pool[2005];
void split(Node *x)
{
Node *y=&pool[++idx];
y->id=idx,y->nxt=x->nxt,x->nxt=y,y->pre=x;
if(y->nxt) y->nxt->pre=y;
for(int i=L;i<x->siz();i++) y->ins((x->vc)[i]),id[(x->vc)[i]]=idx;
(x->vc).erase((x->vc).begin()+L,(x->vc).end());
}
Node* get(int x)
{
for(Node *i=hed;i;i=i->nxt) if(i->id==id[x]) return i;
return nullptr;
}
void build()
{
hed=&pool[++idx],hed->id=idx;
Node *t=hed;
for(int i=1;i<=n;i++)
{
t->ins(a[i]),id[a[i]]=idx;
if(t->siz()>=2*L) split(t),t=t->nxt;
}
}
void del(int x)
{
Node* p=get(x);
for(int i=0;i<p->siz();i++) if((p->vc)[i]==x)
{
(p->vc).erase((p->vc).begin()+i);
break;
}
if(!p->siz())
{
if(p==hed) hed=p->nxt,p->nxt->pre=nullptr;
if(p->pre) p->pre->nxt=p->nxt;
if(p->nxt) p->nxt->pre=p->pre;
p->pre=p->nxt=nullptr;
}
}
void ins(int x,int t)
{
Node *p=hed;
for(;p;p=p->nxt)
{
if(t>p->siz()) t-=p->siz();
else break;
}
(p->vc).emplace((p->vc).begin()+t,x),id[x]=p->id;
if(p->siz()>=2*L) split(p);
}
int qrk(int x)
{
int r=0;
Node *p=hed;
for(;p;p=p->nxt)
{
if(id[x]!=p->id) r+=p->siz();
else break;
}
for(int i=0;i<p->siz();i++) if(x==(p->vc)[i]) return r+i;
return -1;
}
int qnum(int x)
{
Node *p=hed;
for(;p;p=p->nxt)
{
if(x>=p->siz()) x-=p->siz();
else return (p->vc)[x];
}
return -1;
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
for(int i=1;i<=n;i++) std::cin>>a[i];
Rope::build();
for(int i=1,x,y;i<=m;i++)
{
std::string op;
std::cin>>op>>x;
if(op[0]=='T') Rope::del(x),Rope::ins(x,0);
if(op[0]=='B') Rope::del(x),Rope::ins(x,n-1);
if(op[0]=='I')
{
std::cin>>y;
int rk=Rope::qrk(x);
Rope::del(x),Rope::ins(x,rk+y);
}
if(op[0]=='A') std::cout<<Rope::qrk(x)<<'\n';
if(op[0]=='Q') std::cout<<Rope::qnum(x-1)<<'\n';
}
}
F. 维护数列
由于文艺块链比较难写,这里使用 FHQ Treap。
插入即新建一棵由要插入的节点组成的树,将原树从插入位置分裂,将三部分合并。
删除即把操作区间删掉,把两边合并。注意需要节点回收。
修改即给操作区间打一个修改 tag。
翻转即给操作区间打一个翻转 tag。
求和即把操作区间分裂出来取根节点的和。
求最大子段和,需要在每个节点维护最大子段和与最大前后缀,合并时与线段树相似,但需要考虑该节点本身。注意最大子段和不能为空,但是最大前后缀可以。
由于有了前后缀信息,所以在翻转的时候需要交换前后缀。
总结一下,在每个节点需要维护它自己的权值、子树和、修改 tag、翻转 tag、最大子段和、最大前缀、最大后缀。
本题拥有丰富的实现细节。
#include <iostream>
#include <cstdlib>
#define N 500005
int n,m,a[N],rt;
struct Node
{ //wt:rand tg1:col tg2:rev
int val,wt,siz,sum,tg1,tg2;
int mx,pre,suf;
} tr[N];
namespace FHQ
{
int ls[N],rs[N],st[N],tp,idx;
int nd(int x)
{
int u=(tp?st[tp--]:++idx);
tr[u].val=x,tr[u].wt=rand(),tr[u].siz=1,ls[u]=rs[u]=0;
tr[u].tg1=-1024,tr[u].tg2=0;
tr[u].sum=tr[u].mx=x;
tr[u].pre=tr[u].suf=std::max(0,x);
return u;
}
void free(int x) {ls[x]=rs[x]=0,st[++tp]=x;}
void pu(int x)
{
tr[x].siz=tr[ls[x]].siz+tr[rs[x]].siz+1;
tr[x].sum=tr[ls[x]].sum+tr[x].val+tr[rs[x]].sum;
tr[x].pre=std::max(0,std::max(tr[ls[x]].pre,tr[ls[x]].sum+tr[x].val+tr[rs[x]].pre));
tr[x].suf=std::max(0,std::max(tr[rs[x]].suf,tr[rs[x]].sum+tr[x].val+tr[ls[x]].suf));
tr[x].mx=std::max(0,tr[ls[x]].suf+tr[rs[x]].pre)+tr[x].val;
if(ls[x]) tr[x].mx=std::max(tr[x].mx,tr[ls[x]].mx);
if(rs[x]) tr[x].mx=std::max(tr[x].mx,tr[rs[x]].mx);
}
void cv(int x,int t)
{
if(!x) return;
tr[x].val=t,tr[x].sum=t*tr[x].siz;
tr[x].pre=tr[x].suf=std::max(0,tr[x].sum);
tr[x].mx=std::max(t,tr[x].sum);
tr[x].tg1=t;
}
void rev(int x)
{
if(!x) return;
std::swap(ls[x],rs[x]),std::swap(tr[x].pre,tr[x].suf);
tr[x].tg2^=1;
}
void pd(int x)
{
if(tr[x].tg1>-1024)
cv(ls[x],tr[x].tg1),cv(rs[x],tr[x].tg1),
tr[x].tg1=-1024;
if(tr[x].tg2)
rev(ls[x]),rev(rs[x]),
tr[x].tg2=0;
}
std::pair<int,int> split(int x,int t)
{
//printf("split %d siz %d %d\n",x,tr[x].siz,t);
if(!x) return {0,0};
pd(x);
if(t<=tr[ls[x]].siz)
{
std::pair<int,int> tmp=split(ls[x],t);
ls[x]=tmp.second,pu(x);
return {tmp.first,x};
}
else
{
std::pair<int,int> tmp=split(rs[x],t-tr[ls[x]].siz-1);
rs[x]=tmp.first,pu(x);
return {x,tmp.second};
}
}
int merge(int x,int y)
{
//printf("merge %d %d\n",x,y);
if(!x||!y) return x|y;
pd(x),pd(y);
if(tr[x].wt<tr[y].wt)
{
rs[x]=merge(rs[x],y),pu(x);
return x;
}
else
{
ls[y]=merge(x,ls[y]),pu(y);
return y;
}
}
void clr(int x)
{
if(!x) return;
clr(ls[x]),clr(rs[x]),free(x);
}
};
signed main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0),std::cout.tie(0);
std::cin>>n>>m;
for(int i=1,x;i<=n;i++) std::cin>>x,rt=FHQ::merge(rt,FHQ::nd(x));
for(int i=1;i<=m;i++)
{
std::string op;
std::cin>>op;
if(op[2]=='S') //INSERT
{
int x,len;
std::cin>>x>>len;
std::pair<int,int> tmp=FHQ::split(rt,x);
for(int i=1,k;i<=len;i++)
std::cin>>k,tmp.first=FHQ::merge(tmp.first,FHQ::nd(k));
rt=FHQ::merge(tmp.first,tmp.second);
}
if(op[2]=='L') //DELETE
{
int x,len;
std::cin>>x>>len;
std::pair<int,int> tmp=FHQ::split(rt,x-1),trt=FHQ::split(tmp.second,len);
FHQ::clr(trt.first);
rt=FHQ::merge(tmp.first,trt.second);
}
if(op[2]=='K') //MAKE-SAME
{
int x,len,v;
std::cin>>x>>len>>v;
std::pair<int,int> tmp=FHQ::split(rt,x-1),trt=FHQ::split(tmp.second,len);
FHQ::cv(trt.first,v);
rt=FHQ::merge(tmp.first,FHQ::merge(trt.first,trt.second));
}
if(op[2]=='V') //REVERSE
{
int x,len;
std::cin>>x>>len;
std::pair<int,int> tmp=FHQ::split(rt,x-1),trt=FHQ::split(tmp.second,len);
FHQ::rev(trt.first);
rt=FHQ::merge(tmp.first,FHQ::merge(trt.first,trt.second));
}
if(op[2]=='T') //GET-SUM
{
int x,len;
std::cin>>x>>len;
std::pair<int,int> tmp=FHQ::split(rt,x-1),trt=FHQ::split(tmp.second,len);
std::cout<<tr[trt.first].sum<<'\n';
rt=FHQ::merge(tmp.first,FHQ::merge(trt.first,trt.second));
}
if(op[2]=='X') //MAX-SUM
std::cout<<tr[rt].mx<<'\n';
}
}

浙公网安备 33010602011771号