平衡树 - Splay 学习笔记

咕咕咕

二分查找树( BST )

\(\operatorname{BST}\) 树满足性质:

  1. 每一个节点关键码 不小于左子树中 任意节点关键码。

  2. 每一个节点关键码 不大于右子树中 任意节点关键码。

  3. 整棵树 中序遍历单调递增

  • 建立:由两个节点( \(+inf~\&~-inf\) )构成 。

  • 查询:从根开始一个个比较,逐渐来到查询的点 。

  • 插入:若已经有了这个点,那么 \(cnt++\) ;否则新建一个叶子节点 。与查询操作类似 。

  • 前驱(后继):先向左(右)走,在一直往右(左)走,直至不能走为止 。

  • 删除:

    • 只有左子树或右子树 :直接删除 \(x\) ,令 \(x\) 的子节点代替 \(x\) 的位置 。

    • 既有左子树又有右子树 :在 \(x\) 子树中找出 \(x\) 的后继 \(next\) ,令 \(next\) 代替 \(x\) 的位置,删除 \(next\)

期望复杂度 \(O(log~n)\) ,但是极易退化为 \(O(n)\)


Treap

即在 \(\operatorname{BST}\) 的基础上加入 旋转 操作 。

每一个节点仅记录了左右子节点,不记录父亲节点 。\(\operatorname{Treap}\) 通过 左旋\(\operatorname{zag}\) )和 右旋\(\operatorname{zig}\) ),通过 随机数据 保持平衡 。\(\operatorname{Treap}\) 给每一个节点赋予了一个随机值,用来满足堆性质 。

对于 插入 操作:同 \(\operatorname{BST}\) 的加入方式,自下而上依次检查,当某个节点不满足大根堆性质时就进行单旋操作 。

特别对于 删除 操作:

  • 如果只有一个儿子:用它的儿子代替它并替代它 。

  • 若果有两个儿子:把需要删除的节点旋转到只有一个儿子 。

总而言之:\(\operatorname{Treap}\) 通过适当的单旋操作,在 维持关键码满足 \(\operatorname{BST}\) 性质 的同时,还 使每个节点上生成的额外权值满足大根堆性质

复杂度:都是 \(O(log~n)\)

对于这道题,要求查询排名,则给每一个节点增加一个值 \(size\) ,在进行 \(ratate\) 操作时也要对 \(size\) 进行更改 。

因为要对 \(size\) 进行修改,所以查询操作用递归的形式 。

$\text{Treap}$ 模板代码
#include<bits/stdc++.h>
using namespace std;
#define infll 0x3f3f3f3f3f3f3f3f
#define inf 0x7f7f7f7f
#define Maxn 200005
typedef long long ll;
inline int rd()
{
	 int x=0;
     char ch,t=0;
     while(!isdigit(ch = getchar())) t|=ch=='-';
     while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
     return x=t?-x:x;
}
int n,m,tot,root;
int a[Maxn],b[Maxn];
struct treap
{
	 int randval,val;
	 int ls,rs;
	 int cnt,siz;
}tree[Maxn];
void pushup(int p)
{
	 tree[p].siz=tree[p].cnt+tree[tree[p].ls].siz+tree[tree[p].rs].siz;
}
int New(int val)
{
	 tree[++tot].val=val,tree[tot].randval=rand();
	 tree[tot].cnt=tree[tot].siz=1;
	 tree[tot].ls=tree[tot].rs=0;
	 return tot;
}
void build()
{
	 New(-inf),New(inf);
	 tree[1].rs=2,root=1;
	 pushup(root);
}
void zag(int &x) // Make ls turn right
{
	 int p=tree[x].ls;
	 tree[x].ls=tree[p].rs,tree[p].rs=x;
	 pushup(x),pushup(p),x=p;
}
void zig(int &x) // Make rs turn left
{
	 int p=tree[x].rs;
	 tree[x].rs=tree[p].ls,tree[p].ls=x;
	 pushup(x),pushup(p),x=p;
}
void Insert(int &p,int val)
{
	 if(!p) { p=New(val); return; }
	 if(val==tree[p].val) { tree[p].cnt++; pushup(p); return; } // 别忘了 pushup 
	 if(val<tree[p].val)
	 {
	 	 Insert(tree[p].ls,val);
	 	 if(tree[p].randval<tree[tree[p].ls].randval) zag(p);
	 }
	 else
	 {
	 	 Insert(tree[p].rs,val);
	 	 if(tree[p].randval<tree[tree[p].rs].randval) zig(p);
	 }
	 pushup(p);
}
void del(int &p,int val)
{
	 if(!p) return;
	 if(val==tree[p].val)
	 {
	 	 if(tree[p].cnt>1) { tree[p].cnt--; pushup(p); return; } // 别忘了 pushup 
	 	 if(tree[p].ls || tree[p].rs)
	 	 {
	 	 	 if(tree[p].rs==0 || tree[tree[p].ls].randval>tree[tree[p].rs].randval)
			   	 zag(p),del(tree[p].rs,val); // 保持大根堆性质 
	 	 	 else zig(p),del(tree[p].ls,val);
	 	 	 pushup(p);
		 }
		 else p=0;
		 return;
	 }
	 if(val<tree[p].val) del(tree[p].ls,val);
	 else del(tree[p].rs,val);
	 pushup(p);
}
int query_val(int p,int rank)
{
	 if(!p) return inf;
	 if(tree[tree[p].ls].siz>=rank) return query_val(tree[p].ls,rank);
	 if(tree[tree[p].ls].siz+tree[p].cnt>=rank) return tree[p].val;
	 return query_val(tree[p].rs,rank-tree[tree[p].ls].siz-tree[p].cnt);
}
int query_rank(int p,int val)
{
	 if(!p) return 0;
	 if(val==tree[p].val) return tree[tree[p].ls].siz+1;
	 if(val<tree[p].val) return query_rank(tree[p].ls,val);
	 return query_rank(tree[p].rs,val)+tree[tree[p].ls].siz+tree[p].cnt;
}
int pre(int val)
{
	 int p=root,ans=1; // 无穷小 
	 while(p)
	 {
	 	 if(val==tree[p].val)
	 	 {
	 	 	 if(tree[p].ls)
			 {
			 	 for(p=tree[p].ls;tree[p].rs;p=tree[p].rs);
			 	 ans=p;
			 }
			 break;
		 }
		 if(tree[p].val<val && tree[p].val>tree[ans].val) ans=p; // 及时更新 
		 if(val<tree[p].val) p=tree[p].ls;
		 else p=tree[p].rs;
	 }
	 return tree[ans].val;
}
int nex(int val)
{
	 int p=root,ans=2; // 无穷大 
	 while(p)
	 {
	 	 if(val==tree[p].val)
	 	 {
	 	 	 if(tree[p].rs)
	 	 	 {
	 	 	 	 for(p=tree[p].rs;tree[p].ls;p=tree[p].ls);
	 	 	 	 ans=p;
			 }
			 break;
		 }
		 if(tree[p].val>val && tree[p].val<tree[ans].val) ans=p;
		 if(val<tree[p].val) p=tree[p].ls;
		 else p=tree[p].rs;
	 }
	 return tree[ans].val;
}
int main()
{
     //freopen(".in","r",stdin);
     //freopen(".out","w",stdout);
	 n=rd(),build();
	 int opt,x;
	 for(int i=1;i<=n;i++)
	 {
	 	 opt=rd(),x=rd();
	 	 if(opt==1) Insert(root,x);
	 	 if(opt==2) del(root,x);
	 	 if(opt==3) printf("%d\n",query_rank(root,x)-1);
	 	 if(opt==4) printf("%d\n",query_val(root,x+1));
	 	 if(opt==5) printf("%d\n",pre(x));
	 	 if(opt==6) printf("%d\n",nex(x));
	 }
     //fclose(stdin);
     //fclose(stdout);
     return 0;
}

Splay

两种写法

综合上述情况,决定写两份模板代码(暂时只用了 namespace

代码中特别要注意的地方
  1. 查找 \(x\) 的前、后驱时要判断有无 \(val\)\(x\) 的节点!不能直接返回前、后驱。

  2. 记得每次操作进行中、进行后(视不同函数而定)\(\text{Splay}\)

  3. 清楚数组下标是节点编号,不是 \(val\)

  4. 及时 \(\text{update}\) 更新信息

  5. 父子节点之间有双向的连接,缺一不可

  6. \(\text{kth}\) 中)要判断查询的 $k $是否正好为 \(siz[ls]+\dots\) 所以应该判断 \(k\le 0\)

  7. \(\text{del}\) 中)清楚 \(root\) 以及各种信息的更改顺序,防止在赋值之前信息已经被清空

  8. \(\text{del}\) 中)删除有两个儿子的根节点是应该先 \(\text{Splay}\) 前驱 \(pre()\) ,再连接右子树。

    • (在 \(\text{Splay(pre())}\) 后,\(pre()\) 成为根的同时原先的根 \(rt\) 会成为根的右儿子,同时 \(rt\) 原先的右儿子会变为左儿子)
  9. \(\text{Rank}\) 中)在没有 \(val\)\(x\) 的节点是返回 \(1\)

  10. \(\text{Rank}\) 中)\(rank\) 是比 \(x\) 小的个数 \(+1\) !!!

  11. \(\text{Rank}\) 中)查找节点时小心子节点是空的!此时直接返回

  12. \(\text{Splay}\) 中)如果节点 \(x\) 已经在根节点,不需要修改。

  13. \(\text{Splay},\dots\) 中)及时更新 \(root\)

伸展树(即 \(\text{Splay}\) )可以用于维护数与数之间的关系,也可以用于维护区间上的问题。

如果出现类似【模板】普通平衡树一样处理第 \(k\) 大数、排名等,需要在 \(\text{Insert}\) 等操作中合并节点,成为一颗以权值关键字的类 \(\text{BST}\) ,方便查找第几大之类。

一般使用的函数:

  • \(\text{Insert}\) :按照权值大小找到应该插入的位置插入节点。
  • \(\text{Del}\) :删去一个权值为 \(val\) 的数。
  • \(\text{kth}\) :即所有数中第 \(k\) 大的数。
  • \(\text{Rank}\) :这一个数的排名(比这个数小的数的个数加一)。
$\text{Splay}$ 模板 $1$
namespace splay_number
{
	 int root,siz,ch[Maxn][2],fa[Maxn];
	 struct Node { ll val; int cnt,siz; int lay; }tree[Maxn];
	 #define ls ch[x][0]
	 #define rs ch[x][1]
	 inline int get(int x) { return x==ch[fa[x]][1]; }
	 inline void init(int x) { ls=rs=fa[x]=tree[x].cnt=tree[x].val=tree[x].siz=0; }
	 inline void pushup(int x)
 	 {
		 tree[x].siz=tree[ls].siz+tree[rs].siz+tree[x].cnt;
		 // 根据更新父节点信息 
	 }
	 inline void pushdown(int x){}
	 inline void rotate(int x)
	 {
		 register int y=fa[x],z=fa[y],k=get(x);
		 pushdown(y),pushdown(x);
		 ch[y][k]=ch[x][k^1];
		 if(ch[x][k^1]) fa[ch[x][k^1]]=y;
		 fa[y]=x,ch[x][k^1]=y,fa[x]=z;
		 if(z) ch[z][y==ch[z][1]]=x;
		 pushup(y),pushup(x);
	 }
	 inline void splay(int x,int rt=0)
	 {
		 if(x==rt) return;
		 for(int f=fa[x];f!=rt;rotate(x),f=fa[x])
		 	 if(fa[f]!=rt) rotate((get(f)==get(x))?f:x);
		 if(!rt) root=x;
	 }
	 inline int pre() { int x=ch[root][0]; while(rs) x=rs; return x; }
	 inline int nex() { int x=ch[root][1]; while(ls) x=ls; return x; }
	 inline int kth(int k)
	 {
		 for(register int x=root;x;)
		 {
		 	 pushdown(x);
		 	 if(ls && k<=tree[ls].siz) x=ls;
		 	 else
		 	 {
		 	 	 k-=tree[x].cnt+tree[ls].siz;
		 	 	 if(k<=0) return x; // 一般返回下标,如非特殊情况不改为其他 
		 	 	 x=rs;
			 }
		 }
		 return 0;
	 }
	 inline int Rank(ll val)
	 {
		 register int ret=0,x;
		 for(x=root;x;)
		 {
		 	 if(val<tree[x].val) { if(!ls) { ret+=1; break; } x=ls; }
		 	 else if(val==tree[x].val) { ret+=tree[ls].siz+1; break; }
		 	 else
		 	 {
		 	 	 ret+=tree[ls].siz+tree[x].cnt;
		 	 	 if(!rs) { ret+=1; break; }
		 	 	 x=rs;
			 }
		 }
		 splay(x);
		 return ret?ret:1;
	 }
	 inline void Insert(ll x)
	 {
		 register int cur=root,f=0;
		 while(cur && tree[cur].val!=x) f=cur,cur=ch[cur][x>tree[cur].val];
		 if(cur) tree[cur].cnt++;
		 else
		 {
		 	 cur=++siz;
		 	 if(f) ch[f][x>tree[f].val]=cur;
		 	 init(cur);
		 	 tree[cur].val=x,fa[cur]=f;
		 	 tree[cur].siz=tree[cur].cnt=1;
		 }
		 splay(cur);
	 }
	 inline void Del(ll val)
	 {
		 Rank(val); // 可以根据所得到的信息改为 kth 等 
		 register int cur=root;
		 if(tree[root].cnt>1) tree[root].cnt-=1,pushup(root);
		 else if(!ch[root][0] && !ch[root][1]) init(root),root=0;
		 else if(!ch[root][0]) root=ch[root][1],fa[root]=0,init(cur);
		 else if(!ch[root][1]) root=ch[root][0],fa[root]=0,init(cur);
		 else
		 {
		 	 int Last=pre();
		 	 splay(Last);
			 ch[Last][1]=ch[cur][1];
		 	 fa[ch[cur][1]]=Last;
			 init(cur); // 注意顺序!! 
			 pushup(Last);
		 }
	 }
	 inline int Seperate(int l,int r) { l=kth(l),r=kth(r+2),splay(l,0),splay(r,l); return ch[r][0]; }
	 void print(int p) // 调试 
	 {
		 pushdown(p);
		 if(ch[p][0]) print(ch[p][0]);
		 printf("%lld ",tree[p].val);
		 if(ch[p][1]) print(ch[p][1]);
	 }
}
using namespace splay_number;

但是如果出现【模板】文艺平衡树[NOI2005] 维护数列等用于区间查询、翻转等操作时,我们将原数组中的下标作为关键字,使用 \(\text{build}\) 操作加入节点。

一般使用的函数:

  • \(\text{build}\) :用于建立一段区间(非常平衡的完全二叉树),可以用去加入一段区间。
  • \(\text{Find}\) (即上一种情况中的 \(kth\) ) :用在区间数上变为了找到整个序列从左到右的第 \(val\) 个数,将它旋转的根。
  • \(\text{split}\) :提取出一段区间。
$\text{Splay}$ 模板 $2$
namespace splay_segment
{
	 int root,All,ch[Maxn][2],fa[Maxn];
	 int id[Maxn],a[Maxn];
	 queue<int> q; // 冗余节点回收 
	 struct Node { int siz,val,MAX,l_MAX,r_MAX,sum,chan_tag,rev_tag; }tree[Maxn];
	 #define ls ch[x][0]
	 #define rs ch[x][1]
	 inline int get(int x) { return x==ch[fa[x]][1]; }
	 inline void init(int x) { ls=rs=fa[x]=tree[x].chan_tag=tree[x].rev_tag=0; }
	 inline int pre() { int x=ch[root][0]; while(rs) x=rs; return x; }
	 inline int nex() { int x=ch[root][1]; while(ls) x=ls; return x; }
	 void recycle(int x) { if(!x) return; recycle(ls),recycle(rs),init(x),q.push(x); }
	 inline void pushup(int x)
 	 {
		 tree[x].siz=tree[ls].siz+tree[rs].siz+1;
		 tree[x].sum=tree[ls].sum+tree[rs].sum+tree[x].val;
		 tree[x].MAX=max(tree[ls].r_MAX+tree[x].val+tree[rs].l_MAX,max(tree[ls].MAX,tree[rs].MAX));
		 tree[x].l_MAX=max(tree[ls].l_MAX,tree[ls].sum+tree[x].val+tree[rs].l_MAX);
		 tree[x].r_MAX=max(tree[rs].r_MAX,tree[rs].sum+tree[x].val+tree[ls].r_MAX);
	 }
	 inline void pushdown(int x)
	 {
	 	 if(tree[x].chan_tag)
		 // 注意区间赋值的 tag 只能为 1 或 0 不能赋值为 val 
	 	 {
	 	 	 register int tmp=tree[x].val; tree[x].chan_tag=0;
	 	 	 if(ls)
	 	 	 {
	 	 	 	 tree[ls].chan_tag=1,tree[ls].val=tmp,tree[ls].sum=tmp*tree[ls].siz;
	 	 	 	 if(tmp>0) tree[ls].MAX=tree[ls].l_MAX=tree[ls].r_MAX=tree[ls].sum;
	 	 	 	 else tree[ls].MAX=tmp,tree[ls].l_MAX=tree[ls].r_MAX=0;
			 }
			 if(rs)
			 {
	 	 	 	 tree[rs].chan_tag=1,tree[rs].val=tmp,tree[rs].sum=tmp*tree[rs].siz;
	 	 	 	 if(tmp>0) tree[rs].MAX=tree[rs].l_MAX=tree[rs].r_MAX=tree[rs].sum;
	 	 	 	 else tree[rs].MAX=tmp,tree[rs].l_MAX=tree[rs].r_MAX=0;
			 }
		 }
		 if(tree[x].rev_tag)
		 {
		 	 tree[x].rev_tag=0;
		 	 if(ls) tree[ls].rev_tag^=1;
		 	 if(rs) tree[rs].rev_tag^=1;
		 	 swap(ch[ls][0],ch[ls][1]),swap(tree[ls].l_MAX,tree[ls].r_MAX);
		 	 swap(ch[rs][0],ch[rs][1]),swap(tree[rs].l_MAX,tree[rs].r_MAX);
		 }
	 }
	 inline void rotate(int x)
	 {
		 register int y=fa[x],z=fa[y],k=get(x);
		 pushdown(y),pushdown(x);
		 ch[y][k]=ch[x][k^1];
		 if(ch[x][k^1]) fa[ch[x][k^1]]=y;
		 fa[y]=x,ch[x][k^1]=y,fa[x]=z;
		 if(z) ch[z][y==ch[z][1]]=x;
		 pushup(y),pushup(x);
	 }
	 inline void splay(int x,int rt=0)
	 {
		 if(x==rt) return;
		 for(int f=fa[x];f!=rt;rotate(x),f=fa[x])
		 	 if(fa[f]!=rt) rotate((get(f)==get(x))?f:x);
		 if(!rt) root=x;
	 }
	 inline int Find(int k)
	 {
		 for(register int x=root;x;)
		 {
		 	 pushdown(x);
		 	 if(ls && k<=tree[ls].siz) x=ls;
		 	 else
		 	 {
		 	 	 k-=tree[ls].siz+1;
		 	 	 if(k<=0) return x;
		 	 	 x=rs;
			 }
		 }
		 return 0;
	 }
	 inline int split(int l,int r) { l=Find(l),r=Find(r+2),splay(l,0),splay(r,l); return ch[r][0]; }
	 int build(int F,int nl,int nr)
	 {
		 int mid=(nl+nr)>>1,x=id[mid],fx=id[F];
		 if(nl==nr)
		 {
		 	 tree[x].siz=1,tree[x].MAX=tree[x].sum=tree[x].val=a[mid];
		 	 tree[x].l_MAX=tree[x].r_MAX=max(a[mid],0);
		 }
		 if(nl<mid) ch[x][0]=build(mid,nl,mid-1);
		 if(mid<nr) ch[x][1]=build(mid,mid+1,nr);
		 tree[x].val=a[mid],fa[x]=fx,pushup(x);
		 return x;
	 }
	 inline void Insert(int pos,int tot)
	 {
	 	 for(int i=1;i<=tot;i++) a[i]=rd();
	 	 for(int i=1;i<=tot;i++)
	 	 	 if(!q.empty()) id[i]=q.front(),q.pop();
	 	 	 else id[i]=++All;
	 	 int rt=build(0,1,tot),l=Find(pos+1),r=Find(pos+2);
	 	 splay(l,0),splay(r,l),fa[rt]=r,ch[r][0]=rt;
		 pushup(r),pushup(l);
	 }
	 inline void Delete(int l,int r)
	 {
	 	 int rt=split(l,r),F=fa[rt];
	 	 recycle(rt),ch[F][0]=0;
		 pushup(F),pushup(fa[F]);
	 }
	 inline void change(int l,int r,int c)
	 {
	 	 int rt=split(l,r);
		 tree[rt].chan_tag=1,tree[rt].val=c,tree[rt].sum=c*tree[rt].siz;
		 if(c>0) tree[rt].MAX=tree[rt].l_MAX=tree[rt].r_MAX=tree[rt].sum;
		 else tree[rt].MAX=c,tree[rt].l_MAX=tree[rt].r_MAX=0;
		 pushup(fa[rt]),pushup(fa[fa[rt]]);
	 }
	 inline void Reverse(int l,int r)
	 {
	 	 int x=split(l,r);
	 	 tree[x].rev_tag^=1;
	 	 swap(ls,rs),swap(tree[x].l_MAX,tree[x].r_MAX);
	 	 pushup(fa[x]),pushup(fa[fa[x]]);
	 }
	 inline int query_sum(int l,int r) { return tree[split(l,r)].sum; }
	 inline int query_MAX(int l,int r) { return tree[split(l,r)].MAX; }
	 void print(int x) { if(!x) return; pushdown(x),print(ls),printf("%d ",tree[x].val),print(rs); }
}
// 任何修改操作都要反馈到根!!! 
using namespace splay_segment;

应用 \(\rightarrow\) \(\text{LCT}\) 预备知识 P3391 【模板】文艺平衡树

FHQ 非旋 Treap

FHQ-Treap学习笔记——万万没想到

(这里都用了 struct 封装)

模板 $1$
struct FHQ_number
{
	 #define ls tree[p].pl
	 #define rs tree[p].pr
	 int All,root;
	 queue<int> Trash; // 回收站(垃圾桶) 
	 struct NODE { int pl,pr,siz,rnd; ll val; } tree[Maxn];
	 inline void init(int p)
	 	 { tree[p].rnd=rand(); ls=rs=0; tree[p].siz=1; }
	 inline int New(ll val)
	 {
	 	 int ret;
	 	 if(!Trash.empty()) ret=Trash.front(),Trash.pop();
		 else ret=++All;
		 init(ret),tree[ret].val=val;
		 return ret;
	 }
	 inline void pushup(int p)
	 	 { tree[p].siz=tree[ls].siz+tree[rs].siz+1; }
	 void split(int p,ll val,int &x,int &y)
	 {
	 	 if(!p) { x=y=0; return; }
	 	 if(tree[p].val<=val) x=p,split(rs,val,rs,y);
	 	 else y=p,split(ls,val,x,ls);
	 	 pushup(p);
	 }
	 int merge(int x,int y)
	 {
	 	 if(!x || !y) return x|y;
	 	 if(tree[x].rnd<tree[y].rnd)
		 	 { tree[x].pr=merge(tree[x].pr,y),pushup(x); return x; }
		 else
		 	 { tree[y].pl=merge(x,tree[y].pl),pushup(y); return y; }
	 }
 	 int x,y,z;
	 inline void Insert(ll val)
	 	 { split(root,val,x,y),root=merge(merge(x,New(val)),y); }
	 inline void Delete_All(ll val)
	 	 { split(root,val,z,x),split(x,val-1,x,y),root=merge(x,z); }
	 inline void Delete_one(ll val)
	 {
	 	 split(root,val,x,z),split(x,val-1,x,y);
	 	 Trash.push(y),y=merge(tree[y].pl,tree[y].pr);
	 	 root=merge(merge(x,y),z);
	 }
	 inline int Rank(ll val)
	 {
	 	 split(root,val-1,x,y);
	 	 int ret=tree[x].siz+1;
	 	 root=merge(x,y);
	 	 return ret;
	 }
	 inline int kth(int p,int k) // attention!!这里返回的是下标 
	 {
	 	 while(p)
	 	 {
	 	 	 if(k<=tree[ls].siz) p=ls;
	 	 	 else if(k==tree[ls].siz+1) break;
	 	 	 else k-=tree[ls].siz+1,p=rs;
		 }
		 return p;
	 }
	 inline ll pre(ll val)
	 {
	 	 split(root,val-1,x,y);
	 	 ll ret=tree[kth(x,tree[x].siz)].val;
	 	 root=merge(x,y);
	 	 return ret;
	 }
	 inline ll nex(ll val)
	 {
	 	 split(root,val,x,y);
	 	 ll ret=tree[kth(y,1)].val;
	 	 root=merge(x,y);
	 	 return ret;
	 }
}T1;
版本 $2$ (还不能够实现巨大多操作,留坑待填)
struct FHQ_segment
{
	 #define ls tree[p].pl
	 #define rs tree[p].pr
	 int All,root;
	 queue<int> Trash; // 回收站(垃圾桶) 
	 struct NODE { int pl,pr,siz,rnd,tag; ll val; } tree[Maxn];
	 inline void init(int p)
	 	 { tree[p].rnd=rand(); ls=rs=tree[p].tag=0; tree[p].siz=1; }
	 inline int New(ll val)
	 {
	 	 int ret;
	 	 if(!Trash.empty()) ret=Trash.front(),Trash.pop();
		 else ret=++All;
		 init(ret),tree[ret].val=val;
		 return ret;
	 }
	 inline void pushup(int p)
	 	 { tree[p].siz=tree[ls].siz+tree[rs].siz+1; }
	 inline void pushdown(int p)
	 {
	 	 if(tree[p].tag)
	 	 {
	 	 	 swap(tree[ls].pl,tree[ls].pr),tree[ls].tag^=1;
	 	 	 swap(tree[rs].pl,tree[rs].pr),tree[rs].tag^=1;
	 	 	 tree[p].tag=0;
		 }
	 }
	 void split(int p,int k,int &x,int &y)
	 {
	 	 if(!p) { x=y=0; return; }
	 	 pushdown(p);
	 	 if(tree[ls].siz<k) x=p,split(rs,k-tree[ls].siz-1,rs,y);
	 	 else y=p,split(ls,k,x,ls);
	 	 pushup(p);
	 }
	 int merge(int x,int y)
	 {
	 	 if(!x || !y) return x|y;
	 	 if(tree[x].rnd<tree[y].rnd)
		 {
		 	 pushdown(x);
		 	 tree[x].pr=merge(tree[x].pr,y);
			 pushup(x);
			 return x;
		 }
		 else
		 {
		 	 pushdown(y);
		 	 tree[y].pl=merge(x,tree[y].pl);
			 pushup(y);
			 return y;
		 }
	 }
 	 int x,y,z,tp;
 	 int sta[Maxn],a[Maxn],tmp[Maxn];
 	 inline int build(int tot)
 	 {
 	 	 tp=0;
		 for(int i=1;i<=tot;i++) a[i]=i;
 	 	 for(int i=1;i<=tot;i++)
 	 	 {
 	 	 	 x=New(a[i]);
 	 	 	 if(tree[x].rnd>tree[sta[tp]].rnd) tree[sta[tp]].pr=x,sta[++tp]=x;
 	 	 	 else
 	 	 	 {
 	 	 	 	 while(tp && tree[sta[tp]].rnd<=tree[x].rnd) tp--;
 	 	 	 	 if(tp) tree[sta[tp]].pr=x;
 	 	 	 	 tree[x].pl=sta[tp+1];
 	 	 	 	 sta[++tp]=x;
			 }
		 }
		 while(tp) pushup(sta[tp--]);
		 return sta[1];
	 }
	 inline void Reverse(int l,int r)
	 {
	 	 split(root,r,x,z),split(x,l-1,x,y);
	 	 swap(tree[y].pl,tree[y].pr),tree[y].tag^=1;
	 	 root=merge(merge(x,y),z);
	 }
	 void print(int p)
	 {
	 	 pushdown(p);
	 	 if(ls) print(ls);
	 	 printf("%lld ",tree[p].val);
	 	 if(rs) print(rs);
	 }
}T2;
posted @ 2021-09-13 11:06  EricQian06  阅读(121)  评论(0编辑  收藏  举报