【fhq treap】【平衡树】
fhq不仅比splay跑得快,还比splay好写得多。
Code模板
#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read()
{
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') {w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=~(x-1);
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=1e5+100;
int n,tot,rt;
struct fhq{
int siz,ls,rs,val,rd;
}t[N];
int newnode(int val)
{
tot++;t[tot].siz=1;t[tot].ls=t[tot].rs=0;
t[tot].val=val;t[tot].rd=rand();
return tot;
}
void pushup(int x)
{
t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
}
void split(int now,int val,int &x,int &y)
{
if(now==0) {x=y=0;return; }
if(t[now].val<=val) x=now,split(t[now].rs,val,t[now].rs,y);
else y=now,split(t[now].ls,val,x,t[now].ls);
pushup(now);
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(t[x].rd<t[y].rd) {t[x].rs=merge(t[x].rs,y);pushup(x);return x; }
else {t[y].ls=merge(x,t[y].ls); pushup(y);return y; }
}
void insert(int val)
{
int x,y;
split(rt,val,x,y);
rt=merge(x,merge(newnode(val),y));
}
void del(int val)
{
int x,y,z;
split(rt,val,x,z);split(x,val-1,x,y);
rt=merge(x,merge(merge(t[y].ls,t[y].rs),z));
}
int findval(int k)
{
int now=rt;
while(now)
{
if(k==t[t[now].ls].siz+1) return t[now].val;
if(k<=t[t[now].ls].siz) now=t[now].ls;
else k-=t[t[now].ls].siz+1,now=t[now].rs;
}
}
signed main()
{
n=read();
for(int i=1;i<=n;++i)
{
int op=read(),x=read(),y,z;
if(op==1) insert(x);
else if(op==2) del(x);
else if(op==3) split(rt,x-1,y,z),write(t[y].siz+1),puts(""),rt=merge(y,z);
else if(op==4) write(findval(x)),puts("");
else if(op==5) split(rt,x-1,y,z),rt=y,write(findval(t[y].siz)),puts(""),rt=merge(y,z);
else if(op==6) split(rt,x,y,z),rt=z,write(findval(1)),puts(""),rt=merge(y,z);
}
return 0;
}
笛卡尔树线性建树
void build()
{
int lst;
for(int i=1;i<=n;++i)
{
lst=0;
while(top&&p[stk[top]]>p[i]) pushup(lst=stk[top--]);
if(top) rs[stk[top]]=i;
ls[i]=lst;stk[++top]=i;
}
while(top) pushup(stk[top--]);
}
说一些想到的关于平衡树的细节:
- fhq的随机值rand()在linux系统下为02^31-1,在windows系统下为032767,虽然fhq的rand就算溢出也不影响,但为保险起见,直接写rang就可了。
- 有时空间不够需要用内存回收,这时注意要将用过的节点清零。
- pushup,pushdown不要忘记
- 想清楚split按照排名还是数值分裂
- 对于初始序列建树,暴力做是nlogn,笛卡尔建树是O(N),还有一种方法,就是类似线段树的建树,复杂度不明确,但根据计算在2e5到2e6范围内是接近线性的,等以后深入学习后可能会来更新。Update:实测了一下这道题,这种方法仅比笛卡尔树慢几毫秒。
- 笛卡尔树建树时,是利用线性时间,构造出整棵树的形态,信息的维护,既可以在笛卡尔树构造时更新,也可以在构造出整棵树后用1遍dfs合并完所有信息,利用笛卡尔树,可以更快的插入一段区间和初始化。
- 笛卡尔建树用的栈和空间回收用的栈要分清楚。。。
- 时刻注意左右儿子为0时,是否需要特判某些东西
- newnode和和build函数,记得最后返回节点
例题
分析
这道题的操作都是平衡树的经典操作,只有查询P和Q没见过,其中Q是查询一段区间的字符种类,可以在fhq节点上用一个大小为26的bool数组维护即可,但因为字符种类数很少,可以直接用一个二进制数来保存,合并时,直接按位或。
P操作要查初始序列中字符现在的位置。可以考虑在建出初始平衡树后,经过各种操作后,同一个字符的节点下标不变。那么通过这个下标,可以一直向跳父亲,沿途累加答案。但操作中还涉及到区间翻转操作,如果不把这个点的祖先节点的标记全都下传,是无法求出正确答案的,因此,这颗fhq还需对每个点保存一个父亲节点,查询时,从该点向上用一个栈记录他所有的祖先,从上到下pushdown,就容易算出它的排名了。
那么如何维护父亲信息呢?
考虑到父亲信息只有在split和merge中会改变,我们可以在split和merge中额外增加父亲参数,但这样做的话,与我们平常写的fhq差别较大,可能需要在代码中修改很多处。
实际上有一个更方便的做法,我们转换思想,不再考虑在子节点处更新fa信息,而是在父节点处更新。那么这个过程其实与pushup是十分相似的,因此可以直接在pushup中加入:
if(t[x].ls) t[t[x].ls].fa=x;
if(t[x].rs) t[t[x].rs].fa=x;
即可。这样对于每个fa信息被修改的点,它的父亲在pushup时,就能把它的fa信息更新了。但这样还有个小问题,对于每次split和merge后的根节点,其信息是有可能错误的,因为其没有父亲可以更新他。但我们可以先不用管它,因为我们只在查询P操作时,会用到fa信息,在用到时,把rt节点的fa置为0即可。
代码
#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read()
{
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') {w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=~(x-1);
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=2e6+100;
int n,m,vis[N],tot,rt;
struct node{
int ls,rs,rev,val,sum,rd,siz,fa;
}t[N];
char s[N];
int newnode(int val)
{
int x=++tot;
t[x].val=val,t[x].sum=1<<val,t[x].rd=rand(),t[x].siz=1;
return x;
}
void pushup(int x)
{
t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
t[x].sum=(1<<t[x].val)|t[t[x].ls].sum|t[t[x].rs].sum;
if(t[x].ls) t[t[x].ls].fa=x;
if(t[x].rs) t[t[x].rs].fa=x;
}
void pushdown(int x)
{
if(t[x].rev)
{
t[x].rev=0;
swap(t[t[x].ls].ls,t[t[x].ls].rs);
swap(t[t[x].rs].ls,t[t[x].rs].rs);
t[t[x].ls].rev^=1;t[t[x].rs].rev^=1;
}
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(t[x].rd<t[y].rd) {pushdown(x);t[x].rs=merge(t[x].rs,y);pushup(x);return x;}
else {pushdown(y);t[y].ls=merge(x,t[y].ls);pushup(y);return y;}
}
int build(int l,int r)
{
if(l==r) {
int x=newnode(s[l]-'a');
return x;
}
int mid=l+r>>1;
int x=build(l,mid),y=build(mid+1,r);
return merge(x,y);
}
void split(int now,int k,int &x,int &y)
{
if(now==0) {x=y=0;return;}
pushdown(now);
if(k<=t[t[now].ls].siz) y=now,split(t[now].ls,k,x,t[y].ls),pushup(y);
else x=now,split(t[now].rs,k-t[t[now].ls].siz-1,t[x].rs,y),pushup(x);
}
void insert(int pos,int val)
{
int x,y;
split(rt,pos,x,y);//t[x].fa=t[y].fa=0;
rt=merge(merge(x,newnode(val)),y);
}
void del(int pos)
{
int x,y,z;
split(rt,pos,y,z);
split(y,pos-1,x,y);
if(y<=n) vis[y]=1;
rt=merge(x,z);
}
void reverse(int l,int r)
{
int x,y,z;
split(rt,r,y,z);split(y,l-1,x,y);
swap(t[y].ls,t[y].rs);t[y].rev^=1;
rt=merge(x,merge(y,z));
}
int stk[N],top;
int findrk(int pos)
{
t[rt].fa=0;top=0;int x=pos;
while(x) stk[++top]=x,x=t[x].fa;
while(top) pushdown(stk[top--]);
int res=t[t[pos].ls].siz+1;
while(t[pos].fa) {
if(t[t[pos].fa].rs==pos) res+=t[t[t[pos].fa].ls].siz+1;
pos=t[pos].fa;
}
return res;
}
int findval(int pos)
{
int x,y,z;
split(rt,pos,y,z);split(y,pos-1,x,y);
int res=t[y].val;
rt=merge(merge(x,y),z);
return res;
}
int query(int l,int r)
{
int x,y,z;
split(rt,r,y,z);split(y,l-1,x,y);
int res=t[y].sum,sum=0;
rt=merge(merge(x,y),z);
while(res)sum++,res-=res&-res;
return sum;
}
signed main()
{
n=read();m=read();
scanf("%s",s+1);
rt=build(1,n);
for(int i=1;i<=m;++i)
{
char op[2];int x,u,v,w;
scanf("%s",op);
x=read();
if(op[0]=='I'){
char c;scanf("%c",&c);
insert(x,c-'a');
}
else if(op[0]=='D') del(x);
else if(op[0]=='R') reverse(x,read());
else if(op[0]=='P') {
if(vis[x]) puts("0");
else write(findrk(x)),puts("");
}
else if(op[0]=='T') putchar('a'+findval(x)),puts("");
else write(query(x,read())),puts("");
}
return 0;
}

浙公网安备 33010602011771号