fhq Treap
时间:2019-06-12
本文章向大家介绍fhq Treap,主要包括fhq Treap使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
我吹爆fhq,磨了一下午无旋treap,怕遗忘,写点细节。
- fhq treap中有重复的点,它们呈一条链排列,所以del时不能完全砍掉:z树是一条相同权值点的链。下方第6行,只会将z根这一点砍掉(调用merge实际上会直接返回左子树的编号,见代码)
1 void del(int &root,int val) 2 { 3 int x=0,y=0,z=0; 4 split(root,x,y,val); 5 split(x,x,z,val-1); 6 merge(z,tr[z].lc,tr[z].rc); //重复的点 7 merge(x,x,z); 8 merge(root,x,y); 9 }
- 权值分裂和位置分裂(a kth 都<=k)能达到同样的效果,像文艺平衡树这种只保留位置的只能用位置分裂。以下两种分裂:
void split(int x,int &a,int &b,int val) //权值分裂 { if(!x){ a=b=0; return; } if(tr[x].val<=val){ a=x; //x的左子树都能放到a的左子树 split(tr[x].rc,tr[a].rc,b,val); //递归找a的右子树(rc一定大于fa[rc])和b(↓) } else{ b=x; split(tr[x].lc,a,tr[b].lc,val); } up(x); }
1 void split(int x,int &a,int &b,int k) 2 { 3 if(!x){ 4 a=b=0; 5 return; 6 } 7 down(x); 8 if(k<=tr[tr[x].lc].siz){ //a已满先找b //如果x恰好是划分点(恰好x左子树为a),则x及x的右子树为b树,递归下一层将tr[x].lc付给a 9 b=x; 10 split(tr[x].lc,a,tr[b].lc,k); 11 } 12 else{ 13 a=x; 14 split(tr[x].rc,tr[a].rc,b,k-tr[tr[x].lc].siz-1); 15 } 16 up(x); //eee 17 }
个人感觉位置分裂比较难理解且难记呃,9行实际上可以理解为a树能被x的左子树填满,但此时x的左子树中还可能有大于rank>k的值,所以显然不能直接将x.lc直接付给a。
fhq treap的中序遍历是升序,文艺平衡树的不是,因为交换了子树,从而使siz[]的含义发生变化,使其直接代表了位置。
附:
#include<cstdio> #include<ctime> #include<cstring> #include<algorithm> #include<cstdlib> #define MAXN 200005 #define reg register #define F(i,a,b) for(i=a;i<=b;++i) using namespace std; int o,root; struct TR{ int val,rd,siz,lc,rc; }tr[MAXN]; int newnode(int val) { int x=++o; tr[x].val=val; tr[x].rd=rand(); tr[x].siz=1; return x; } void up(int x) { tr[x].siz=tr[tr[x].lc].siz+tr[tr[x].rc].siz+1; } void split(int x,int &a,int &b,int val) //权值分裂和位置分裂效果相同,应用条件不同 { if(!x){ a=b=0; return; } if(tr[x].val<=val){ a=x; //x的左子树都能放到a的左子树 split(tr[x].rc,tr[a].rc,b,val); //递归找a的右子树(rc一定大于fa[rc])和b(↓) } else{ b=x; split(tr[x].lc,a,tr[b].lc,val); //b eee } up(x); } void merge(int &x,int a,int b) { if(a*b==0){ x=a+b; return; } if(tr[a].rd<tr[b].rd){ x=a; //x及x的左子树确定,b放在x(a)的右子树上(b的每个节点都大于a) merge(tr[x].rc,tr[a].rc,b); } else{ x=b; //x及x的右子树确定,同理 merge(tr[x].lc,a,tr[b].lc); } up(x); } void insert(int &root,int val) { int x=0,y=0; split(root,x,y,val); merge(x,x,newnode(val)); merge(root,x,y); } void del(int &root,int val) { int x=0,y=0,z=0; split(root,x,y,val); split(x,x,z,val-1); merge(z,tr[z].lc,tr[z].rc); //重复的点 merge(x,x,z); merge(root,x,y); } int kth(int rt,int k) { int x=rt; while(tr[tr[x].lc].siz+1!=k) { if(k<=tr[tr[x].lc].siz) x=tr[x].lc; else k-=tr[tr[x].lc].siz+1,x=tr[x].rc; } return tr[x].val; } int rak(int &root,int val) { //x,y树形态? int x=0,y=0; split(root,x,y,val-1); int ans=tr[x].siz+1; merge(root,x,y); return ans; } int pre(int &root,int val) { int x=0,y=0; split(root,x,y,val-1); int ans=kth(x,tr[x].siz); merge(root,x,y); return ans; } int nex(int &root,int val) { int x=0,y=0; split(root,x,y,val); int ans=kth(y,1); merge(root,x,y); return ans; } void dfs(int now){ if(!now) return; dfs(tr[now].lc); printf("%d ",tr[now].val); dfs(tr[now].rc); } int main() { srand(time(0)); int n; scanf("%d",&n); reg int op,x; while(n--) { scanf("%d%d",&op,&x); switch(op){ case 1:insert(root,x);break; case 2:del(root,x);break; case 3:printf("%d\n",rak(root,x));break; case 4:printf("%d\n",kth(root,x));break; case 5:printf("%d\n",pre(root,x));break; case 6:printf("%d\n",nex(root,x));break; case 7:dfs(root);puts("");break; //升序 } } return 0; }
#include<cstdio> #include<ctime> #include<cstring> #include<algorithm> #include<cstdlib> #define MAXN 100005 #define reg register #define F(i,a,b) for(i=a;i<=b;++i) using namespace std; int o,f[MAXN],root; struct TR{ int lc,rc,siz,rd,val; }tr[MAXN]; int newnode(int val) { int x=++o; tr[x].val=val; tr[x].rd=rand(); tr[x].siz=1; return x; } void up(int k) { tr[k].siz=tr[tr[k].lc].siz+tr[tr[k].rc].siz+1; } void down(int k) { if(f[k]){ swap(tr[k].lc,tr[k].rc); f[tr[k].lc]^=1; f[tr[k].rc]^=1; f[k]=0; } } void split(int x,int &a,int &b,int k) { if(!x){ a=b=0; return; } down(x); if(k<=tr[tr[x].lc].siz){ //a已满先找b //如果x恰好是划分点(恰好x左子树为a),则x及x的右子树为b树,递归下一层将tr[x].lc付给a b=x; split(tr[x].lc,a,tr[b].lc,k); } else{ a=x; split(tr[x].rc,tr[a].rc,b,k-tr[tr[x].lc].siz-1); } up(x); //eee } void merge(int &x,int a,int b) { if(a*b==0){ x=a+b; return; } down(a); down(b); if(tr[a].rd<tr[b].rd){ x=a; merge(tr[x].rc,tr[a].rc,b); } else{ x=b; merge(tr[x].lc,a,tr[b].lc); } up(x); } void work(int l,int r) { int x=0,y=0,z=0; split(root,x,y,r); split(x,x,z,l-1); f[z]^=1; merge(x,x,z); merge(root,x,y); } void dfs(int x){ if(!x) return; down(x); dfs(tr[x].lc); printf("%d ",tr[x].val); dfs(tr[x].rc); } int main() { srand(time(0)); int n; reg int i,a,b,m; scanf("%d%d",&n,&m); F(i,1,n) merge(root,root,newnode(i)); while(m--) { scanf("%d%d",&a,&b); work(a,b); } dfs(root); return 0; }
原文地址:https://www.cnblogs.com/hzoi-yzh/p/11011287.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法