口胡fhq treap

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/83900390

口胡

其实就是传说中的无旋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;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/83900390