口胡fhq treap
阿新 • • 發佈:2019-01-04
口胡
其實就是傳說中的無旋treap。鑑於我總是寫不出無旋treap,但是無旋treap又意外的好用,而且之前這個無旋treap板子是在遠航大佬的部落格上學的,但是遠航貌似現在沒時間維護部落格,所以我就發一篇方便以後自己翻翻。其實遠航大佬還是寫的很好的,如果後續有維護的話建議看遠航的文章入門。。
無旋treap好打,同樣可以支援按權值維護或者維護序列,核心函式一般頂多四個,所以碼量相比於splay確實會少很多,在一般需要支援一個比較簡單的操作但set卻不支援時,是一個很好的替代品。不信看下面普通平衡樹的程式碼。
本質就是維護一個以rand為權的小根堆,由於rand是我們自己隨機出來的,所以樹高期望是 的。
合併
無論是維護權值還是序列,都通用一個合併函式,而這個函式其實是和可並堆相似的,由於x完全y左邊,所以才可以做到 的合併。我直到今年暑假還一直誤以為它可以 合併任意兩棵按權值排的無旋treap。
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(rnd[x]<rnd[y])
{
ch[x][1]=merge(ch[x][1],y);
pushup(x);return x;
}
else
{
ch[y][0]=merge(x,ch[y][0]);
pushup(y);return y;
}
}
分裂
維護權值的分裂長這樣,其中k的含義是把小於等於k的都放進x子樹中,大於的則在y子樹中。
void split(int now,int k,int &x,int &y)
{
if(!now) x=y=0;
else
{
if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y);
else y=now,split(ch[now][0],k,x,ch[now][0]);
pushup(now);
}
}
而維護序列的長這樣,k的含義是把前k個放進x子樹中,其他放進y子樹中。
void split(int now,int k,int &x,int &y)
{
if(!now) x=y=0;
else
{
if(k<=s[ch[now][0]]) y=now,split(ch[now][0],k,x,ch[now][0]);
else x=now,split(ch[now][1],k-s[ch[now][0]]-1,ch[now][1],y);
pushup(now);
}
}
第k小
在找前驅/後繼時,為了方便實現程式碼我們會需要這麼一個函式。本質和splay的kth一致,就不再贅述。
int kth(int now,int k)
{
while(1)
{
if(k<=s[ch[now][0]]) now=ch[now][0];
else
{
if(k==s[ch[now][0]]+1) return now;
else k-=s[ch[now][0]]+1,now=ch[now][1];
}
}
}
其他操作
其他的很多操作可以自己yy的,通常的套路就是先split成兩棵子樹,然後再做操作,最後合併回來即可。
比如維護權值的時候插入一個x,那麼就是按x分裂一下,在新增一個節點,再依次合併。
Code
普通平衡樹的程式碼。
#include <cstdlib>
#include <cstdio>
#define rg register
#define pushup(x) s[x]=s[ch[x][0]]+s[ch[x][1]]+1
using namespace std;
typedef long long ll;
const int maxn=100010;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
int m,sz,rt,op,a,x,y,z,val[maxn],rnd[maxn],s[maxn],ch[maxn][2];
int new_node(int x)
{
s[++sz]=1;val[sz]=x;rnd[sz]=rand();
return sz;
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(rnd[x]<rnd[y])
{
ch[x][1]=merge(ch[x][1],y);
pushup(x);return x;
}
else
{
ch[y][0]=merge(x,ch[y][0]);
pushup(y);return y;
}
}
void split(int now,int k,int &x,int &y)
{
if(!now) x=y=0;
else
{
if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y);
else y=now,split(ch[now][0],k,x,ch[now][0]);
pushup(now);
}
}
int kth(int now,int k)
{
while(1)
{
if(k<=s[ch[now][0]]) now=ch[now][0];
else
{
if(k==s[ch[now][0]]+1) return now;
else k-=s[ch[now][0]]+1,now=ch[now][1];
}
}
}
int main()
{
srand(19260817);
read(m);
while(m--)
{
read(op);read(a);
if(op==1){split(rt,a,x,y);rt=merge(merge(x,new_node(a)),y);}
if(op==2)
{
split(rt,a,x,z);split(x,a-1,x,y);
y=merge(ch[y][0],ch[y][1]);
rt=merge(merge(x,y),z);
}
if(op==3){split(rt,a-1,x,y);printf("%d\n",s[x]+1);rt=merge(x,y);}
if(op==4) printf("%d\n",val[kth(rt,a)]);
if(op==5)
{
split(rt,a-1,x,y);
printf("%d\n",val[kth(x,s[x])]);
rt=merge(x,y);
}
if(op==6)
{
split(rt,a,x,y);
printf("%d\n",val[kth(y,1)]);
rt=merge(x,y);
}
}
return 0;
}