1. 程式人生 > >口胡fhq treap

口胡fhq treap

口胡

其實就是傳說中的無旋treap。鑑於我總是寫不出無旋treap,但是無旋treap又意外的好用,而且之前這個無旋treap板子是在遠航大佬的部落格上學的,但是遠航貌似現在沒時間維護部落格,所以我就發一篇方便以後自己翻翻。其實遠航大佬還是寫的很好的,如果後續有維護的話建議看遠航的文章入門。。

無旋treap好打,同樣可以支援按權值維護或者維護序列,核心函式一般頂多四個,所以碼量相比於splay確實會少很多,在一般需要支援一個比較簡單的操作但set卻不支援時,是一個很好的替代品。不信看下面普通平衡樹的程式碼。

本質就是維護一個以rand為權的小根堆,由於rand是我們自己隨機出來的,所以樹高期望是 O

( log n ) O(\log n) 的。

合併

無論是維護權值還是序列,都通用一個合併函式,而這個函式其實是和可並堆相似的,由於x完全y左邊,所以才可以做到 O

( log n ) O(\log n) 的合併。我直到今年暑假還一直誤以為它可以 O (
log n ) O(\log n)
合併任意兩棵按權值排的無旋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;
}