我写博客是给自己看的,所以别喷我丑
treap是一种特别神奇的东西,从英文字母中可以看出其的本质:tree+heap
tree——BST二叉搜索树,heap——堆
首先二叉搜索树就不讲了(一句话:满足左子树所有结点的值<根结点的值<右子树所有结点的值)
在最优的情况下,是O(n lg n),这是非常非常优秀的时间复杂度
但是如果单单维护二叉搜索树,当它是一条链的时候时间复杂度是O(n^2),这与暴力是一样的
所以无良的出题人甚至会把你卡的怀疑人生
这个时候,你会怎么想?
一条链的树可以通过变换形态在本质不变(中序遍历不变)的情况下变成一棵平衡二叉树
如果这时候能把子树的根换掉不出现一条链的情况就好了
那该怎么办呢?
脑洞极大的人想出了一种方法,再给每个点随机一个值,然后用堆的形式维护那个值,
这样一条链的可能就差不多和彩票中奖一样。
这样思想就讲完了,是不是很简单啊?
如何维护呢?换根,跟splay一样,菜鸡先朦朦胧胧地学了splay的思想至今还没写过
维护的时候就设计到一个技巧:zig/zag(左旋/右旋)
inline void zig(int &x,int y)
{
lc[x]=rc[y],rc[y]=x;
siz[y]=siz[x],siz[x]=siz[lc[x]]+siz[rc[x]]+c[x];
x=y;
} //lc表示左儿子,rc表示右儿子,siz表示内部结点数,c表示相同权值的数的个数(因为普通平衡树中有重复),x为原来的根,y为后来的根
inline void zag(int &x,int y)
{
rc[x]=lc[y],lc[y]=x;
siz[y]=siz[x],siz[x]=siz[lc[x]]+siz[rc[x]]+c[x];
x=y;
}
例题(唯一的例题): Luogu P3369 【模板】普通平衡树
https://www.luogu.org/problemnew/show/P3369
这题涉及了六个基本操作:
1、插入 首先按照BST里的插入方法找到合法的位置插入,在随机该结点在堆上的值,然后换根维护堆的性质就好了
void ins(int &x,int k)
{
if(!x)
{
x=++cnt,tre[x]=k,hea[x]=rand()%2000000000,siz[x]=1,c[x]=1;
return;
}
siz[x]++;
if(tre[x]==k) c[x]++;
else if(tre[x]<k)
{
ins(rc[x],k);
if(hea[rc[x]]<hea[x]) zag(x,rc[x]);
}else
{
ins(lc[x],k);
if(hea[lc[x]]<hea[x]) zig(x,lc[x]);
}
} //tre是tree的缩写,是BST意义上的值,hea是heap的缩写,是堆意义上的值,怎么说怎么做即可
2、删除:找到删除的结点,如果有多个可能解,就删掉1个(定义);如果它只有1个儿子,直接强行链接儿子和父亲;如果在叶子结点,直接删掉。
但是都不是呢?
强行把结点转到可以按以上操作处理的位置进行操作
void del(int &x,int k)
{
if(tre[x]==k)
{
if(c[x]>1) c[x]--,siz[x]--;
else if(!lc[x]||!rc[x]) x=lc[x]+rc[x];
else if(hea[lc[x]]<hea[rc[x]]) zig(x,lc[x]),del(x,k);
else zag(x,rc[x]),del(x,k); //一定要是x,不然会MLE
return;
}
siz[x]--;
if(k<tre[x]) del(lc[x],k);
else del(rc[x],k);
}
3、查询x数的排名:这和二叉搜索树一样,比较它与目标结点,如果小了就遍历左子树,如果大了就遍历右子树,否则返回
int find(int x,int k)
{
if(tre[x]==k) return siz[lc[x]]+1;
if(tre[x]>k) return find(lc[x],k);
return c[x]+siz[lc[x]]+find(rc[x],k);
}
4、查找第K大的数,也和二叉搜索树一样,略
int kth(int x,int k)
{
if(siz[lc[x]]<k&&k<=siz[lc[x]]+c[x]) return tre[x];
if(siz[lc[x]]>=k) return kth(lc[x],k);
return kth(rc[x],k-siz[lc[x]]-c[x]);
}
5、找前驱:目标结点大于等于查询的数,就遍历左子树,否则可能是自己或者其右子树中某个结点
int pre(int x,int k)
{
if(!x) return -1e9; //注意一定要足够小
if(tre[x]>=k) return pre(lc[x],k);
return max(pre(rc[x],k),tre[x]);
}
6、找后驱:同上,略
int nxt(int x,int k)
{
if(!x) return 1e9;
if(tre[x]<=k) return nxt(rc[x],k);
return min(nxt(lc[x],k),tre[x]);
}
以上就是菜鸡写的treap,放一个完整的代码
#include<cstdio>
#include<iostream>
#include<ctime>
#include<algorithm>
using namespace std;
const int N=1e6+5;
int n,r;
struct A
{
int cnt,r,lc[N],rc[N],tre[N],hea[N],siz[N],c[N];
inline void zig(int &x,int y)
{
lc[x]=rc[y],rc[y]=x;
siz[y]=siz[x],siz[x]=siz[lc[x]]+siz[rc[x]]+c[x];
x=y;
}
inline void zag(int &x,int y)
{
rc[x]=lc[y],lc[y]=x;
siz[y]=siz[x],siz[x]=siz[lc[x]]+siz[rc[x]]+c[x];
x=y;
}
void ins(int &x,int k)
{
if(!x)
{
x=++cnt,tre[x]=k,hea[x]=rand()%2000000000,siz[x]=1,c[x]=1;
return;
}
siz[x]++;
if(tre[x]==k) c[x]++;
else if(tre[x]<k)
{
ins(rc[x],k);
if(hea[rc[x]]<hea[x]) zag(x,rc[x]);
}else
{
ins(lc[x],k);
if(hea[lc[x]]<hea[x]) zig(x,lc[x]);
}
}
void del(int &x,int k)
{
if(tre[x]==k)
{
if(c[x]>1) c[x]--,siz[x]--;
else if(!lc[x]||!rc[x]) x=lc[x]+rc[x];
else if(hea[lc[x]]<hea[rc[x]]) zig(x,lc[x]),del(x,k);
else zag(x,rc[x]),del(x,k);
return;
}
siz[x]--;
if(k<tre[x]) del(lc[x],k);
else del(rc[x],k);
}
int find(int x,int k)
{
if(tre[x]==k) return siz[lc[x]]+1;
if(tre[x]>k) return find(lc[x],k);
return c[x]+siz[lc[x]]+find(rc[x],k);
}
int kth(int x,int k)
{
if(siz[lc[x]]<k&&k<=siz[lc[x]]+c[x]) return tre[x];
if(siz[lc[x]]>=k) return kth(lc[x],k);
return kth(rc[x],k-siz[lc[x]]-c[x]);
}
int pre(int x,int k)
{
if(!x) return -1e9;
if(tre[x]>=k) return pre(lc[x],k);
return max(pre(rc[x],k),tre[x]);
}
int nxt(int x,int k)
{
if(!x) return 1e9;
if(tre[x]<=k) return nxt(rc[x],k);
return min(nxt(lc[x],k),tre[x]);
}
}treap;
int read()
{
int ret=0; bool f=0;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ret=(ret<<1)+(ret<<3)+ch-'0',
ch=getchar();
return f?-ret:ret;
}
void write(int x)
{
if(x<0)
{
putchar('-'),write(-x);
return;
}
if(x/10) write(x/10);
putchar(x%10+48);
}
int main()
{
srand(time(NULL));
n=read();
while(n--)
{
int t=read(),x=read();
if(t==1) treap.ins(treap.r,x);
else if(t==2) treap.del(treap.r,x);
else if(t==3) write(treap.find(treap.r,x)),puts("");
else if(t==4) write(treap.kth(treap.r,x)),puts("");
else if(t==5) write(treap.pre(treap.r,x)),puts("");
else if(t==6) write(treap.nxt(treap.r,x)),puts("");
}
return 0;
}