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';
	}
}
posted @ 2025-06-28 13:05  整齐的艾萨克  阅读(12)  评论(0)    收藏  举报