$fhq$ $treap$
\(fhq~~treap\)
\(fhq~Treap\) 是一种很好用的数据结构。它很像 $ Treap$ ,但它也可以支持区间操作,同时它也可以进行可持久化,它甚至还很好写。面对这么好的数据结构,怎么能不学一学呢?
这个ppt可能是起源?
如果你已经看过了上面那个 \(ppt\),应该知道无旋 \(Treap\) 的思想是不修改,只新建和重用。
\(\text{代替} ~treap\)
\(split\)
首先它有一个有趣的操作 \(split\),意思是把一棵树分裂成两棵树。嗯...?其实相当于新建两棵树,它们拼起来等于原来那棵树。两种说法有区别吗?当然有啦,注意,“不修改!“,如果直接把树变成两棵,显然就是修改了,而用两棵新的树表示它,对它并没有什么影响。这里可能说的有点啰嗦了,但是我认为对于后面的理解还是有好处的。不过,分裂后可能确实会对原树进行一些更改,但是并不影响它二叉搜索树的性质。
分裂也有两种:
1、将权值小于等于k的节点分裂出来。
void split (int n,int k,int &x,int &y)
{
if(!n) { x=y=0; return; }
if(v[n]<=k) x=n,split(ch[x][1],k,ch[x][1],y);
else y=n,split(ch[y][0],k,x,ch[y][0]);
update(n);
}
2、将前k个节点分裂出来。
void split (int n,int k,int &x,int &y)
{
if(!n) { x=y=0; return; }
if(d[n]) pushdown(n);
if(s[ ch[n][0] ]<k)
x=n,split(ch[x][1],k-s[ ch[x][0] ]-1,ch[x][1],y);
else
y=n,split(ch[y][0],k,x,ch[y][0]);
update(n);
}
第一个主要用于维护数集,第二个主要用于维护序列。
\(merge\)
字面意思,就是把两棵树合并。不过这个操作限制挺多的,要求某一棵树完全小于另一树,也就是说,在不考虑随机权值的情况下可以随便合并。为了维护平衡,我们按照随机权值来合并。看代码:
int mer (int x,int y)
{
if(!x||!y) return x|y;
if(r[x]<r[y])
{
ch[x][1]=mer(ch[x][1],y);
update(x); return x;
}
else
{
ch[y][0]=mer(x,ch[y][0]);
update(y); return y;
}
}
\(insert\)
剩下的操作真是一个比一个简单。如何插入?首先把小于它的分裂出来命名为 \(a\) ,剩下的命名为 \(b\),然后给它新建一个节点 \(c\),最后 \(rt=mer(mer(a,b),c)\);
\(delete\)
小于它的分出来,大于它的分出来,等于它的随便删一个,再按顺序 \(merge\) 回来;
\(rank\)
比它小的分出来,查一下 \(size\) 再+1;
\(kth\)
如果无聊的话,可以尝试分裂前 \(k\) 大,然而直接在树上走着找就可以了,因为树高是 \(log\) 的;
\(pre\)
比它小的分裂出来,查询最大的那个数;
\(nex\)
比它大的分裂出来,查询最小的那个数;
\(code:\)
# include <cstdio>
# include <iostream>
# include <cstdlib>
# include <ctime>
# define R register int
# define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<20,stdin),S==T)?EOF:*S++)
char BB[1 << 20], *S = BB, *T = BB;
using namespace std;
const int N=100005;
int ch[N][2],r[N],s[N],v[N];
int n,rt,cnt,a,b,opt,x;
void update (int x) { s[x]=1+s[ ch[x][0] ]+s[ ch[x][1] ]; }
inline int rand()
{
static int seed=2003;
return seed=int(seed*48271LL%2147483647);
}
int mer (int x,int y)
{
if(!x||!y) return x|y;
if(r[x]<r[y])
{
ch[x][1]=mer(ch[x][1],y);
update(x); return x;
}
else
{
ch[y][0]=mer(x,ch[y][0]);
update(y); return y;
}
}
void spilt (int n,int k,int &x,int &y)
{
if(!n) { x=y=0; return; }
if(v[n]<=k) x=n,spilt(ch[x][1],k,ch[x][1],y);
else y=n,spilt(ch[y][0],k,x,ch[y][0]);
update(n);
}
int kth (int n,int k)
{
while(1)
{
if(k<=s[ ch[n][0] ]) n=ch[n][0];
else if(k==s[ ch[n][0] ]+1) return n;
else k-=s[ ch[n][0] ]+1,n=ch[n][1];
}
}
int newnode (int x)
{
s[++cnt]=1;
r[cnt]=rand();
v[cnt]=x;
return cnt;
}
int read()
{
R x=0,f=1;
char c=getchar();
while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
int main()
{
n=read();
for (R i=1;i<=n;++i)
{
opt=read(); x=read();
if(opt==1)
{
spilt(rt,x,a,b);
rt=mer(mer(a,newnode(x)),b);
}
else if(opt==2)
{
spilt(rt,x,a,b);
spilt(a,x-1,a,rt);
rt=mer(ch[rt][0],ch[rt][1]);
rt=mer(mer(a,rt),b);
}
else if(opt==3)
{
spilt(rt,x-1,a,b);
printf("%d\n",s[a]+1);
rt=mer(a,b);
}
else if(opt==4)
{
printf("%d\n",v[kth(rt,x)]);
}
else if(opt==5)
{
spilt(rt,x-1,a,b);
printf("%d\n",v[kth(a,s[a])]);
rt=mer(a,b);
}
else
{
spilt(rt,x,a,b);
printf("%d\n",v[kth(b,1)]);
rt=mer(a,b);
}
}
return 0;
}
\(\text{代替} ~Splay\)
这里的 \(Splay\) 特指用来做序列操作的那种;
其实,想一想就能知道 \(fhq~treap\) 应该是很适合序列操作的,尤其是 \(split\) 操作,简直就是为了序列操作量身定制的。
\(reverse\)
因为是序列上的某一段,所以要按照大小分裂。先把前 \(l-1\) 个分裂出来,再把第二段的前 \(r-l+1\) 个分出来,打上翻转标记,最后按顺序 \(merge\) 就可以了。在 \(split\) 和 \(merge\) 里,都要记得下放标记;
\(code:\)
# include <cstdio>
# include <iostream>
# define R register int
using namespace std;
const int N=100005;
int n,m,cnt,a,b,c,rt,x,y;
int s[N],r[N],v[N],d[N],ch[N][2];
inline int rd()
{
static int seed=2003;
return seed=int(seed*48271LL%2147483647);
}
int newnode (int x)
{
s[++cnt]=1;
r[cnt]=rd();
v[cnt]=x;
d[cnt]=0;
return cnt;
}
void update (int x) { s[x]=1+s[ ch[x][0] ]+s[ ch[x][1] ]; }
void pushdown (int x)
{
swap(ch[x][0],ch[x][1]);
d[x]=0;
if(ch[x][0]) d[ ch[x][0] ]^=1;
if(ch[x][1]) d[ ch[x][1] ]^=1;
}
int mer (int x,int y)
{
if(!x||!y) return x|y;
if(r[x]<r[y])
{
if(d[x]) pushdown(x);
ch[x][1]=mer(ch[x][1],y);
update(x); return x;
}
else
{
if(d[y]) pushdown(y);
ch[y][0]=mer(x,ch[y][0]);
update(y); return y;
}
}
void spilt (int n,int k,int &x,int &y)
{
if(!n) { x=y=0; return; }
if(d[n]) pushdown(n);
if(s[ ch[n][0] ]<k)
{
x=n;
spilt(ch[x][1],k-s[ ch[x][0] ]-1,ch[x][1],y);
}
else
{
y=n;
spilt(ch[y][0],k,x,ch[y][0]);
}
update(n);
}
void write (int x)
{
if(d[x]) pushdown(x);
if(ch[x][0]) write(ch[x][0]);
printf("%d ",v[x]);
if(ch[x][1]) write(ch[x][1]);
}
int main()
{
scanf("%d%d",&n,&m);
for (R i=1;i<=n;++i)
rt=mer(rt,newnode(i));
for (R i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
spilt(rt,x-1,a,b);
spilt(b,y-x+1,b,c);
d[b]^=1;
rt=mer(mer(a,b),c);
}
write(rt);
return 0;
}
看到这里,你可能会觉得这个东西没什么用处啊,学个 \(Splay\) 不就好了?
嗯。。。你的 \(Splay\) 会可持久化吗?
\(\text{可持久化}~treap\)
# include <cstdio>
# include <iostream>
# define R register int
# define inf 2147483647
using namespace std;
const int N=500005;
const int M=500005*50;
int n,cnt,a,b,c;
int rt[N],ch[M][2],s[M],r[M],v[M];
inline int rd()
{
static int seed=2003;
return seed=int(seed*48271LL%2147483647);
}
int newnode (int x)
{
s[++cnt]=1;
v[cnt]=x;
r[cnt]=rd();
return cnt;
}
void update (int x) { s[x]=1+s[ ch[x][0] ]+s[ ch[x][1] ]; }
void cop (int a,int b)
{
ch[a][0]=ch[b][0];
ch[a][1]=ch[b][1];
r[a]=r[b];
s[a]=s[b];
v[a]=v[b];
}
void split (int n,int k,int &x,int &y)
{
if(!n) { x=y=0; return; }
if(v[n]<=k)
{
x=++cnt; cop(x,n);
split(ch[x][1],k,ch[x][1],y);
update(x);
}
else
{
y=++cnt; cop(y,n);
split(ch[y][0],k,x,ch[y][0]);
update(y);
}
}
int mer (int x,int y)
{
if(!x||!y) return x+y;
if(r[x]<r[y])
{
int t=++cnt; cop(t,x);
ch[t][1]=mer(ch[t][1],y);
update(t); return t;
}
else
{
int t=++cnt; cop(t,y);
ch[t][0]=mer(x,ch[t][0]);
update(t); return t;
}
}
int kth (int n,int k)
{
while(1)
{
if(k<=s[ ch[n][0] ]) n=ch[n][0];
else if(k==s[ ch[n][0] ]+1) return n;
else k-=s[ ch[n][0] ]+1,n=ch[n][1];
}
}
int main()
{
scanf("%d",&n);
int las,opt,x;
for (R i=1;i<=n;++i)
{
scanf("%d%d%d",&las,&opt,&x);
switch(opt)
{
case 1:
{
split(rt[las],x,a,b);
rt[i]=mer(mer(a,newnode(x)),b);
break;
}
case 2:
{
split(rt[las],x-1,a,b);
split(b,x,b,c);
b=mer(ch[b][0],ch[b][1]);
rt[i]=mer(mer(a,b),c);
break;
}
case 3:
{
rt[i]=rt[las];
split(rt[i],x-1,a,b);
printf("%d\n",s[a]+1);
rt[i]=mer(a,b);
break;
}
case 4:
{
rt[i]=rt[las];
printf("%d\n",v[ kth(rt[i],x) ]);
break;
}
case 5:
{
rt[i]=rt[las];
split(rt[i],x-1,a,b);
if(!a) printf("%d\n",inf);
else printf("%d\n",v[ kth(a,s[a]) ]);
rt[i]=mer(a,b);
break;
}
case 6:
{
rt[i]=rt[las];
split(rt[i],x,a,b);
if(!b) printf("%d\n",-inf);
else printf("%d\n",v[ kth(b,1) ]);
rt[i]=mer(a,b);
break;
}
}
}
return 0;
}
\(\text{可持久化}~Splay\):
# include <cstdio>
# include <iostream>
# define R register int
# define ll long long
# define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<20,stdin),S==T)?EOF:*S++)
char BB[1 << 20], *S = BB, *T = BB;
using namespace std;
const int N=200005;
const int M=20000007;
int n,las,opt,a,b,c,cnt;
int rt[N],ch[M][2],siz[M],r[M],rev[M],v[M];
ll ans,x,y,pos,s[M];
inline ll read()
{
ll x=0,f=1;
char c=getchar();
while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); }
while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
inline int rd()
{
static int seed=2003;
return seed=int(seed*48271LL%2147483647);
}
int newnode (int x)
{
siz[++cnt]=1;
s[cnt]=x;
v[cnt]=x;
r[cnt]=rd();
return cnt;
}
void update (int x)
{
siz[x]=1+siz[ ch[x][0] ]+siz[ ch[x][1] ];
s[x]=v[x]+s[ ch[x][0] ]+s[ ch[x][1] ];
}
int cop (int x,int y)
{
siz[x]=siz[y]; r[x]=r[y]; s[x]=s[y];
rev[x]=rev[y]; v[x]=v[y];
ch[x][0]=ch[y][0];
ch[x][1]=ch[y][1];
return x;
}
void pushdown (int x)
{
if(!rev[x]) return;
rev[x]=0;
if(ch[x][0]) ch[x][0]=cop(++cnt,ch[x][0]);
if(ch[x][1]) ch[x][1]=cop(++cnt,ch[x][1]);
swap(ch[x][0],ch[x][1]);
if(ch[x][0]) rev[ ch[x][0] ]^=1;
if(ch[x][1]) rev[ ch[x][1] ]^=1;
}
void split (int n,int k,int &x,int &y)
{
if(!n) { x=y=0; return; }
pushdown(n);
if(k<=siz[ ch[n][0] ])
{
y=++cnt; y=cop(y,n);
split(ch[y][0],k,x,ch[y][0]);
update(y);
}
else
{
x=++cnt; x=cop(x,n);
split(ch[x][1],k-siz[ ch[x][0] ]-1,ch[x][1],y);
update(x);
}
}
int mer (int a,int b)
{
if(!a||!b) return a|b;
pushdown(a); pushdown(b);
if(r[a]<r[b])
{
int t=++cnt; t=cop(t,a);
ch[t][1]=mer(ch[t][1],b);
update(t); return t;
}
else
{
int t=++cnt; t=cop(t,b);
ch[t][0]=mer(a,ch[t][0]);
update(t); return t;
}
}
int main()
{
n=read();
for (R i=1;i<=n;++i)
{
las=read(); opt=read();
switch (opt)
{
case 1:
{
pos=read(); x=read();
pos^=ans; x^=ans;
split(rt[las],pos,a,b);
rt[i]=mer(mer(a,newnode(x)),b);
break;
}
case 2:
{
pos=read(); pos^=ans;
split(rt[las],pos-1,a,b);
split(b,1,b,c);
rt[i]=mer(a,c);
break;
}
case 3:
{
x=read(); y=read();
x^=ans; y^=ans;
split(rt[las],x-1,a,b);
split(b,y-x+1,b,c);
rev[b]^=1;
rt[i]=mer(mer(a,b),c);
break;
}
case 4:
{
x=read(); y=read();
x^=ans; y^=ans;
rt[i]=rt[las];
split(rt[las],x-1,a,b);
split(b,y-x+1,b,c);
ans=s[b];
printf("%lld\n",ans);
break;
}
}
}
return 0;
}