1.基本结构
随机化工具
unsigned int SEED = 19260817; //+1s
inline int Rand(){
SEED=SEED*1103515245+12345;
return SEED/65536;
}
内部成员
struct Treap{
int son[MAXN][2],val[MAXN],fix[MAXN],size[MAXN];
int tot,root;
#define lc son[o][0]
#define rc son[o][1]
}
初始化与构造
void init(){
root=0;//用于判断是否为NULL
tot=1; //所以tot从1遍历
son[0][0]=son[0][1]=val[0]=fix[0]=size[0]=0;
}
int node(int v){
son[tot][0]=son[tot][1]=0;
size[tot]=1;
val[tot]=v; //权值
fix[tot]=Rand(); //附加权值
return tot++;
}
void pu(int o){
size[o]=1+size[lc]+size[rc];
}
2.核心操作
void split(int o,int pivot,int &a,int &b){
if(!o){
a=b=0;
return;
}else if(val[o]<=pivot){ //如果节点权值小于等于pivot
a=o; //o所有左子树放到左边的树
split(rc,pivot,rc,b); //遍历右儿子
}else{
b=o;
split(lc,pivot,a,lc);
}
pu(o);
}
整体而言,这个操作就是把一个Treap分裂为左Treap和右Treap并把根节点分别赋给a b,其中左Treap所有权值总是小于等于pivot,右Treap则大于
并且这个操作复杂度为\(O(logn)\),下面简略说一下过程
来个小实验
int son[MAXN][2],val[MAXN];
#define lc son[o][0]
#define rc son[o][1]
void split(int o,int pivot,int &a,int &b){
if(!o){
a=b=0;
return;
}else if(val[o]<=pivot){
a=o;
split(rc,pivot,rc,b);
}else{
b=o;
split(lc,pivot,a,lc);
}
}
int main(){
val[1]=3,val[2]=2,val[3]=7;
val[4]=1,val[5]=5,val[6]=8;
val[7]=4,val[8]=6;
son[1][0]=2,son[1][1]=3;
son[2][0]=4;
son[3][0]=5,son[3][1]=6;
son[5][0]=7,son[5][1]=8;
int a,b;
split(1,5,a,b);
cout<
对方不想说话,并掏出了...沾满灰尘的破板子
可以看出往左倾斜的方块放在a子树,往右倾斜的放在b子树,并且不断拼接就能分裂成完整的a和b
并且每一次操作就能把一半的子树摆好位置(随机fixed的情况下),所以整个操作流程就是\(O(logn)\)
一般使用还是按size划分比较好,提取区间简单轻松
void split(int o,int k,int &a,int &b){
if(!o){
a=b=0;
return;
}else if(k<=size[lc]){
b=o;
split(lc,k,a,lc);
pu(o);
}else{
a=o;
split(rc,k-size[lc]-1,rc,b);
pu(o);
}
}
int merge(int a,int b){ //保证val比较时a是左子树,b是右子树
if(!a) return b;
if(!b) return a;
if(fix[a]
merge是一个维护堆性质的过程,这个操作是十分的简单粗暴,再上一个小实验
int son[MAXN][2],fix[MAXN];
#define lc son[o][0]
#define rc son[o][1]
int merge(int a,int b){
if(!a)return a;if(!b)return b;
if(fix[a]
(这回画的更丑了)
可以看到其实还是一个分块再合并的过程,看着和斜堆很像,但更为暴力的是这家伙连左右交换都不做(当然它是有随机化buff加成可以为所欲为,而且还要维护val呢)
也就是说这个过程仅仅是通过维护一个堆的性质来合并而已,因为Rand()的存在令它依然是妥妥的\(O(logn)\)
按道理说想要降低常数那说还有一种策略就是把递归换成栈实现的迭代式,对频繁使用的核心操作而言应该是不小的提升,但递归写起来真方便..
3.区间操作
先来个最最简单插入/查询
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
树形操作
BZOJ - 3224
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
区间操作+简单标记
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include