fhq-treap
基操
-
一切splay支持的它都可以(要splay有何用),支持区间加,区间乘,找前驱后继,区间翻转等
-
支持非序列上的操作(以需要排序的为键值)和序列(以中序遍历作为键值)
-
序列可以直接建树,和笛卡尔树一样,时间\(O(n)\)
-
可以可持久化(空间差不开50倍)
-
treap可以启发式合并(\(O(nlg^2n)\)),也可以利用split直接合并,时间复杂度更优,其中如果将\(n\)个大小为1的treap合并在一起时间复杂度\(O(nlgn)\)
int Merge(int x,int y) { if(!x||!y) return x|y; if(rnd[x]>rnd[y]) swap(x,y); int z,w; split(y,c[x],z,w); ls[x]=Merge(ls[x],z); rs[x]=Merge(rs[x],w); return x; }
模板1
set裸题!!!按权值split
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int cnt,c[N],mn[N],mx[N],rnd[N],n,rt,ls[N],rs[N],ans;
inline int New(int x) {
++cnt;
c[cnt]=mn[cnt]=mx[cnt]=x;
rnd[cnt]=rand();
return cnt;
}
inline void up(int p) {
mx[p]=mn[p]=c[p];
if(ls[p]) {
mx[p]=max(mx[p],mx[ls[p]]);
mn[p]=min(mn[p],mn[ls[p]]);
}
if(rs[p]) {
mx[p]=max(mx[p],mx[rs[p]]);
mn[p]=min(mn[p],mn[rs[p]]);
}
}
void split(int p,int k,int &x,int &y) {
if(!p) {
x=y=0; return;
}
if(c[p]<=k) {
x=p; split(rs[p],k,rs[p],y);
} else {
y=p; split(ls[p],k,x,ls[p]);
}
up(p);
}
int merge(int x,int y) {
if(!x||!y) return x|y;
if(rnd[x]<rnd[y]) {
rs[x]=merge(rs[x],y);
up(x);
return x;
}
ls[y]=merge(x,ls[y]);
up(y);
return y;
}
int main() {
scanf("%d",&n);
scanf("%d",&ans);
rt=New(ans);
for(int i=2;i<=n;i++) {
int t; scanf("%d",&t);
int x=0,y=0;
split(rt,t,x,y);
int now=1e9;
if(x) now=min(now,t-mx[x]);
if(y) now=min(now,mn[y]-t);
ans+=now;
rt=merge(x,merge(New(t),y));
}
printf("%d\n",ans);
return 0;
}
千万别再写a[++cnt]=b[cnt]这种脑瘫行为了
模板2
把元素当做键值,按照size分裂(显然)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,cnt,c[N],rnd[N],ls[N],rs[N],r[N],sz[N],rt;
inline void up(int p) {
sz[p]=sz[ls[p]]+sz[rs[p]]+1;
}
void bld(int &p,int l,int r) {
if(l>r) {
p=0; return;
}
int mid=l+r>>1;
p=++cnt; c[p]=mid,rnd[p]=rand();
bld(ls[p],l,mid-1),bld(rs[p],mid+1,r);
up(p);
}
inline void rev(int p) {
swap(ls[p],rs[p]);
r[p]^=1;
}
inline void down(int p) {
if(r[p]) {
if(ls[p]) rev(ls[p]);
if(rs[p]) rev(rs[p]);
r[p]=0;
}
}
void split(int p,int k,int &x,int &y) {
if(!p) {
x=y=0; return;
}
down(p);
if(sz[ls[p]]+1<=k) {
x=p; split(rs[p],k-sz[ls[p]]-1,rs[x],y);
} else {
y=p; split(ls[p],k,x,ls[y]);
}
up(p);
}
int merge(int x,int y) {
if(!x||!y) return x|y;
if(rnd[x]<rnd[y]) {
down(x);
rs[x]=merge(rs[x],y);
up(x);
return x;
} else {
down(y);
ls[y]=merge(x,ls[y]);
up(y);
return y;
}
}
void write(int p) {
down(p);
if(ls[p]) write(ls[p]);
printf("%d ",c[p]);
if(rs[p]) write(rs[p]);
}
int main(){
scanf("%d%d",&n,&m);
bld(rt,1,n);
for(int i=1;i<=m;i++) {
int u,v; scanf("%d%d",&u,&v);
int x,y,z;
split(rt,u-1,x,y);
split(y,v-u+1,y,z);
rev(y);
rt=merge(x,merge(y,z));
}
write(rt);
return 0;
}
Ps:裂开区间\([l,r]\)应该是split(rt,l-1,x,y),split(rt,r-l+1,y,z)
模板3
split和merge时都新拷贝节点
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+5,M=3e7+5;
int cnt,c[M],rt[N],sz[M],rnd[M],ls[M],rs[M];
inline int New(int x) {
c[++cnt]=x,sz[cnt]=1,rnd[cnt]=rand();
return cnt;
}
inline int co(int p) {
c[++cnt]=c[p],sz[cnt]=1,rnd[cnt]=rnd[p],ls[cnt]=ls[p],rs[cnt]=rs[p];
return cnt;
}
inline void up(int p) {
sz[p]=sz[ls[p]]+sz[rs[p]]+1;
}
void split(int p,int k,int &x,int &y) {
if(!p) {
x=y=0; return;
}
if(k>=c[p]) {
x=co(p),split(rs[x],k,rs[x],y);
up(x);
} else {
y=co(p),split(ls[y],k,x,ls[y]);
up(y);
}
}
int merge(int x,int y) {
if(!x||!y) return x|y;
if(rnd[x]<rnd[y]) {
x=co(x);
rs[x]=merge(rs[x],y);
up(x);
return x;
}
y=co(y);
ls[y]=merge(x,ls[y]);
up(y);
return y;
}
inline int rnk(int p,int k) {
int ret=1;
while(p) {
if(c[p]>=k) p=ls[p];
else {
ret+=sz[ls[p]]+1;
p=rs[p];
}
}
return ret;
}
inline int kth(int p,int k) {
while(p) {
if(sz[ls[p]]+1==k) {
return c[p];
}
if(sz[ls[p]]>=k) p=ls[p];
else {
k-=sz[ls[p]]+1;
p=rs[p];
}
}
}
inline int pre(int p,int x) {
int ret=(-1LL<<31)+1;
while(p) {
if(c[p]<x) {
ret=max(ret,c[p]);
p=rs[p];
} else p=ls[p];
}
return ret;
}
inline int suf(int p,int x) {
int ret=(1LL<<31)-1;
while(p) {
if(c[p]>x) {
ret=min(ret,c[p]);
p=ls[p];
} else p=rs[p];
}
return ret;
}
void write(int p) {
if(ls[p]) write(ls[p]);
printf("%d ",c[p]);
if(rs[p]) write(rs[p]);
}
int main() {
freopen("1.in","r",stdin);
int T; scanf("%d",&T); int x,y,z,t;
for(int i=1;i<=T;i++) {
int vi,op,t; scanf("%d%d%d",&vi,&op,&t);
if(op==1) {
split(rt[vi],t,x,y);
rt[i]=merge(x,merge(New(t),y));
} else if(op==2) {
split(rt[vi],t-1,x,y);
split(y,t,y,z);
y=merge(ls[y],rs[y]);
rt[i]=merge(x,merge(y,z));
} else {
rt[i]=rt[vi];
if(op==3) {
printf("%d\n",rnk(rt[i],t));
} else if(op==4) {
printf("%d\n",kth(rt[i],t));
} else if(op==5) {
printf("%d\n",pre(rt[i],t));
} else {
printf("%d\n",suf(rt[i],t));
}
}
}
return 0;
}
例题1
序列上的题,中序遍历为键值
唯一没法知道的是编号,可以记录父亲,将节点值作为编号,然后暴力往上跳,求出他的排名即可
树高为\(O(nlgn)\)
注意中途up时要把自己的fa赋值为0,防止出现无法修改根节点父亲的情况
建树和建笛卡尔树一样
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,ls[N],rs[N],fa[N],sz[N],rnd[N],c[N],rt;
char op[N];
inline void up(int p) {
if(ls[p]) fa[ls[p]]=p;
if(rs[p]) fa[rs[p]]=p;
fa[p]=0;
sz[p]=sz[ls[p]]+sz[rs[p]]+1;
}
void split(int p,int k,int &x,int &y) {
if(!p) {
x=y=0; return;
}
if(sz[ls[p]]<=k-1) {
x=p; split(rs[x],k-sz[ls[p]]-1,rs[x],y);
} else {
y=p; split(ls[y],k,x,ls[y]);
}
up(p);
}
int merge(int x,int y) {
if(!x||!y) return x|y;
if(rnd[x]<rnd[y]) {
rs[x]=merge(rs[x],y);
up(x);
return x;
} else{
ls[y]=merge(x,ls[y]);
up(y);
return y;
}
}
inline int find(int p) {
int ret=sz[ls[p]]+1;
for(;fa[p];p=fa[p]) {
if(rs[fa[p]]==p) {
ret+=sz[ls[fa[p]]]+1;
}
}
return ret;
}
inline int New(int x) {
sz[x]=1,rnd[x]=rand();
return x;
}
int top,st[N];
int main(){
scanf("%d%d",&n,&m); int top=0;
for(int i=1;i<=n;i++) {
scanf("%d",&c[i]);
int u=New(c[i]),lst=0;;
while(top&&rnd[st[top]]>rnd[u]) {
lst=st[top]; up(st[top]); top--;
}
if(top) rs[st[top]]=u;
ls[u]=lst;
st[++top]=u;
}
for(;top;top--) up(st[top]);
rt=st[1];
int x,y,z;
for(int i=1;i<=m;i++) {
int t; scanf("%s%d",op,&t);
if(op[0]=='T') {
int tt=find(t);
split(rt,tt-1,x,y);
split(y,1,y,z);
rt=merge(y,merge(x,z));
} else if(op[0]=='B'){
int tt=find(t);
split(rt,tt-1,x,y);
split(y,1,y,z);
rt=merge(merge(x,z),y);
} else if(op[0]=='I') {
int del; scanf("%d",&del);
int tt=find(t);
split(rt,tt-1,x,y);
split(y,1,y,z);
rt=merge(x,z);
tt+=del-1;
split(rt,tt,x,z);
rt=merge(x,merge(y,z));
} else if(op[0]=='A') {
printf("%d\n",find(t)-1);
} else {
split(rt,t-1,x,y);
split(y,1,y,z);
printf("%d\n",y);
rt=merge(x,merge(y,z));
}
}
return 0;
}