神奇数据结构------fhqTreap
fhqTreap是不需要旋转的平衡树,比splay常数小,代码短,还支持区间操作,而且
可持久化!!!
首先,结点的值val满足是一棵排序二叉树,节点的一个随机值key满足是一个堆(孩子的key比父亲大),因为是随机的,所以整棵树的高度接近于lg n,拆分合并也都是接近O(n lg n)
fhqTreap主要就是两个操作 其他都是无脑拆分合并:
1.split(拆分)
就是把根为now的一棵树拆为根为x的和根为y的两棵树,满足子树x的值都<=k,子树y的值都>k
void split(int now, int &x, int &y, int k) {
if(!now) {x=y=0; return;} //如果带拆分的树为空,拆出来的两棵树也肯定为空
if(tr[now].val<=k) x=now, split(tr[now].r, tr[x].r, y, k); //如果根节点的val<=k,那么左子树的val也就肯定都<=k,因为是排序二叉树,然后x=now,再把now的右子树分成两棵树,val<=k的那颗直接接到x的右子树上(因为x的左子树的val一定都<=k)
else y=now, split(tr[now].l, x, tr[y].l, k); //否则根节点的val肯定>k,那么右子树的val也就肯定都>k,因为是排序二叉树,然后y=now,再把now的左子树分成两棵树,val>k的那颗直接接到y的左子树上(因为y的右子树的val一定都>k)
update(now); //更新一下子树大小(后面会用到)
}
2.merge(合并)
就是把以x为根的树与以y为根的树按key合并,根节点为now
void merge(int &now, int x, int y) {
if(x==0||y==0) {now=x+y; return;} //如果有一颗树为空,那么合并后就是另外一颗树,x+y如果一个为0,那么结果就是另一个
if(tr[x].key<tr[y].key) now=x, merge(tr[now].r, tr[x].r, y); //如果x的key比y小,那么now的左子树=x的左子树(因为x的key比y小,所以x要当y的祖先),now的右子树是x的右子树和y合并的
else now=y, merge(tr[now].l, x, tr[y].l); //否则x的key比y大,那么now的右子树=y的右子树(因为x的key比y大,所以y要当x的祖先),now的左子树是y的左子树和x合并的
update(now); //更新一下子树大小(后面会用到)
}
然后就是一大波操作。。。。。
查找
int find(int now, int rank) { //在根为now的子树找第rank小的
while(tr[tr[now].l].si+1!=rank) //左子树大小+1是根节点的排名
if(tr[tr[now].l].si>=rank) now=tr[now].l; //左子树里找第rank小的
else rank-=tr[tr[now].l].si+1, now=tr[now].r; //右子树里找第rank-tr[tr[now].l].si+1小的,因为去掉左子树和根
return now;
}
插入
void ins(int d) { //插入值为d的节点
int x, y, z=New(d); //新建一个值为d的节点赋给z,z是待插入的结点
split(rt, x, y, d); //把rt(整棵树)拆分成<=d和>d的两颗子树
merge(x, x, z); //x和z合并
merge(rt, x, y); //此时x包含了z,再把x和y合并,就插入了一个新节点
}
删除
void del(int d) { //删除值为d的节点
int x, y, z;
split(rt, x, y, d);
split(x, x, z, d-1); //此时z是值为d的所有节点
merge(z, tr[z].l, tr[z].r); //把z的左子树和右子树合并,相当于把根删掉
merge(x, x, z);
merge(rt, x, y); //合并
}
…剩下的很简单,就看下代码吧P3369 【模板】普通平衡树
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
using namespace std;
const int maxn=100020, INF=(int)(2.1e9), mod=1000007;
struct pj{
int si, val, key, l, r;
} tr[maxn]; int len, rt=1;
int read() {
int x=0, f=1; char c=getchar();
for (; c<'0'||c>'9'; c=getchar()) if(c=='-') f=0;
for (; c>='0'&&c<='9'; c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return f?x:-x;
}
int Random() {return (long long)rand()*rand()%mod;}
int New(int val) {
tr[++len].val=val; tr[len].si=1; tr[len].key=Random(); tr[len].l=tr[len].r=0; return len;
}
void update(int x) {tr[x].si=tr[tr[x].l].si+tr[tr[x].r].si+1;}
void split(int now, int &x, int &y, int k) {
if(!now) {x=y=0; return;}
if(tr[now].val<=k) x=now, split(tr[now].r, tr[x].r, y, k);
else y=now, split(tr[now].l, x, tr[y].l, k);
update(now);
}
void merge(int &now, int x, int y) {
if(x==0||y==0) {now=x+y; return;}
if(tr[x].key<tr[y].key) now=x, merge(tr[now].r, tr[x].r, y);
else now=y, merge(tr[now].l, x, tr[y].l);
update(now);
}
int find(int now, int rank) {
while(tr[tr[now].l].si+1!=rank)
if(tr[tr[now].l].si>=rank) now=tr[now].l;
else rank-=tr[tr[now].l].si+1, now=tr[now].r;
return now;
}
void ins(int d) {
int x, y, z=New(d);
split(rt, x, y, d);
merge(x, x, z);
merge(rt, x, y);
}
void del(int d) {
int x, y, z;
split(rt, x, y, d);
split(x, x, z, d-1);
merge(z, tr[z].l, tr[z].r);
merge(x, x, z);
merge(rt, x, y);
}
void find_pm(int d) {
int x, y;
split(rt, x, y, d-1);
printf("%d\n", tr[x].si+1);
merge(rt, x, y);
}
void find_sz(int k) {
printf("%d\n", tr[find(rt, k)].val);
}
void find_qq(int d) {
int x, y;
split(rt, x, y, d-1);
printf("%d\n", tr[find(x, tr[x].si)].val);
merge(rt, x, y);
}
void find_hj(int d) {
int x, y;
split(rt, x, y, d);
printf("%d\n", tr[find(y, 1)].val);
merge(rt, x, y);
}
int main() {
srand((unsigned)time(0));
int n; n=read(); New(INF); tr[1].si=0;
for (int ty, x; n--; ) {
ty=read(); x=read();
switch(ty) {
case 1: ins(x); break;
case 2: del(x); break;
case 3: find_pm(x); break;
case 4: find_sz(x); break;
case 5: find_qq(x); break;
case 6: find_hj(x); break;
}
}
return 0;
}