题解 P3369 【【模板】普通平衡树】
2024-09-05 21:48:01
在网上某篇神奇的教程和@codesonic 大佬的标程帮助下,我又肝完了Leafy Tree,跑过来写篇题解(好像以前写过一篇?)
什么是Leafy Tree?
Leafy Tree由两种节点组成:辅助节点与叶子节点。
叶子节点储存值,而辅助节点储存左右孩子中大的那个值。
注意:辅助节点必定有两个孩子。
操作如何实现?
拿插入操作举例:
一路向下递归,每次拿左子树最大值与插入值作比较,如果大就往左,如果小就往右。
到底了就插入叶子与辅助。
然后再回溯更新。
这时候就会出一个问题:这个算法很容易被数据卡。
解决方案是引入平衡因子,在适当的时候重建这颗树。
重构方法可以拍扁也可以旋转。
拍扁的方法就是中序遍历一遍然后重新建树(具体可以参考这里),旋转的一会儿会讲。
工具函数
这里是一些简单的重要的工具函数。
- 新建节点
inline void newNode(int &pos,int v){
pos=++cnt,size[pos]=1,val[pos]=v;
}
cnt是总结点个数,size是子树大小,val是值(废话)。
- 复制节点
inline void copyNode(int x,int y){
size[x]=size[y],ls[x]=ls[y],rs[x]=rs[y],val[x]=val[y];
}
没什么好说的
- 合并节点
void merge(int l,int r){
size[++cnt]=size[l]+size[r],val[cnt]=val[r],ls[cnt]=l,rs[cnt]=r;
}
- 旋转
void rotate(int pos,bool flag){
if(flag){
merge(ls[pos],ls[rs[pos]]);
ls[pos]=cnt,rs[pos]=rs[rs[pos]];
}else{
merge(rs[ls[pos]],rs[pos]);
rs[pos]=cnt,ls[pos]=ls[ls[pos]];
}
}
这是重建依赖的旋转函数,左旋右旋看flag。
具体流程没什么好讲的,可以参考splay的左旋与右旋。
- 重建
void maintain(int pos){
if(size[ls[pos]]>size[rs[pos]]*alpha)rotate(pos,0);
else if(size[rs[pos]]>size[ls[pos]]*alpha)rotate(pos,1);
if(size[ls[pos]]>size[rs[pos]]*alpha)rotate(ls[pos],1),rotate(pos,0);
else if(size[rs[pos]]>size[ls[pos]]*alpha)rotate(rs[pos],0),rotate(pos,1);
}
这是重建函数,平衡因子就这题而言取4应该是最快的。
插入操作
void insert(int pos,int v){
if(size[pos]==1){
newNode(ls[pos],min(v,val[pos]));
newNode(rs[pos],max(v,val[pos]));
pushup(pos);
return;
}
if(v>val[ls[pos]])insert(rs[pos],v);
else insert(ls[pos],v);
pushup(pos);
maintain(pos);
}
思路就是之前讲的一路向下递归。
当子树大小为1的时候(到头了)就在底下新建两个节点,一个叶子一个辅助,然后回溯更新。
如果没到头的话就继续递归,然后递归完了就维护一下左右子树的平衡,看看需不需要重建。
删除操作
void erase(int pos,int v){
if(size[pos]==1){
if(ls[father]==pos)copyNode(father,rs[father]);
else copyNode(father,ls[father]);
return;
}
father=pos;
if(v>val[ls[pos]])erase(rs[pos],v);
else erase(ls[pos],v);
pushup(pos);
maintain(pos);
}
删除操作的思路和插入操作一样,一路向下。
需要说明的是father是我们记录的父亲(也可以不这样而是通过传参解决)。
排名查询&排名对应数查询
int kth(int pos,int v){
if(size[pos]==v)return val[pos];
if(v>size[ls[pos]])return kth(rs[pos],v-size[ls[pos]]);
return kth(ls[pos],v);
}
int rank(int pos,int v){
if(size[pos]==1)return 1;
if(v>val[ls[pos]])return rank(rs[pos],v)+size[ls[pos]];
return rank(ls[pos],v);
}
这两个。。。写什么平衡树都会用到肯定大家都会。
然后好像就结束了(Leafy Tree本来码量就不高)
不开O2不加读优大概是311ms左右(竟然还没有我替罪羊树快),应该是写丑了吧。。。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100100;
const int alpha=4;
int n,cnt,father,root;
int val[N<<2],size[N<<2],ls[N<<2],rs[N<<2];
inline void newNode(int &pos,int v){
pos=++cnt,size[pos]=1,val[pos]=v;
}
inline void copyNode(int x,int y){
size[x]=size[y],ls[x]=ls[y],rs[x]=rs[y],val[x]=val[y];
}
void merge(int l,int r){
size[++cnt]=size[l]+size[r],val[cnt]=val[r],ls[cnt]=l,rs[cnt]=r;
}
void rotate(int pos,bool flag){
if(flag){
merge(ls[pos],ls[rs[pos]]);
ls[pos]=cnt,rs[pos]=rs[rs[pos]];
}else{
merge(rs[ls[pos]],rs[pos]);
rs[pos]=cnt,ls[pos]=ls[ls[pos]];
}
}
void maintain(int pos){
if(size[ls[pos]]>size[rs[pos]]*alpha)rotate(pos,0);
else if(size[rs[pos]]>size[ls[pos]]*alpha)rotate(pos,1);
if(size[ls[pos]]>size[rs[pos]]*alpha)rotate(ls[pos],1),rotate(pos,0);
else if(size[rs[pos]]>size[ls[pos]]*alpha)rotate(rs[pos],0),rotate(pos,1);
}
void pushup(int pos){
if(!size[ls[pos]])return;
size[pos]=size[ls[pos]]+size[rs[pos]];
val[pos]=val[rs[pos]];
}
void insert(int pos,int v){
if(size[pos]==1){
newNode(ls[pos],min(v,val[pos]));
newNode(rs[pos],max(v,val[pos]));
pushup(pos);
return;
}
if(v>val[ls[pos]])insert(rs[pos],v);
else insert(ls[pos],v);
pushup(pos);
maintain(pos);
}
void erase(int pos,int v){
if(size[pos]==1){
if(ls[father]==pos)copyNode(father,rs[father]);
else copyNode(father,ls[father]);
return;
}
father=pos;
if(v>val[ls[pos]])erase(rs[pos],v);
else erase(ls[pos],v);
pushup(pos);
maintain(pos);
}
int kth(int pos,int v){
if(size[pos]==v)return val[pos];
if(v>size[ls[pos]])return kth(rs[pos],v-size[ls[pos]]);
return kth(ls[pos],v);
}
int rank(int pos,int v){
if(size[pos]==1)return 1;
if(v>val[ls[pos]])return rank(rs[pos],v)+size[ls[pos]];
return rank(ls[pos],v);
}
int main(){
scanf("%d",&n);
newNode(root,2147483647);
while(n--){
int s,a;
scanf("%d%d",&s,&a);
if(s==1)insert(root,a);
if(s==2)erase(root,a);
if(s==3)printf("%d\n",rank(root,a));
if(s==4)printf("%d\n",kth(root,a));
if(s==5)printf("%d\n",kth(root,rank(root,a)-1));
if(s==6)printf("%d\n",kth(root,rank(root,a+1)));
}
}
最新文章
- java编解码技术,netty nio
- nancy的诊断2
- Rotate Matrix by One
- phantomjs 自动化测试
- 不支持关键字: “userid”。
- yum版本新增包的一般步骤
- Python之路,Day19 - CMDB、CMDB、CMDB
- 2012 T-SQL 新特性 and O2O项目
- Machine Learning——Supervised Learning(机器学习之监督学习)
- 在linux服务器上发布web应用的完整过程
- swift学习 引入三方遇到的问题
- mongodb 案例 ~ 经典故障案例
- 行为链分析zipkin
- ubuntu软件管理
- python中pip和pygame的安装
- SpringBoot结合Swagger2自动生成api文档
- LeetCode题解之Pascal&#39;s Triangle II
- Exp7 网络欺诈技术防范
- 微信公众号开发者模式自定义菜单 node
- 团队博客-第三周:需求改进&;系统设计(科利尔拉弗队)
热门文章
- django patch 解决 [";&#39;15428560000&#39; value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.";]
- WePy--记录使用过程中的一些坑
- http://my.oschina.net/joanfen/blog/160156
- java中Collection 与Collections的区别
- leetcode第一刷_Reverse Linked List II
- Linux 0.11中write实现
- Vuejs2.0学习之二(Render函数,createElement,vm.$slots,函数化组件,模板编译,JSX)
- caffe中lenet_train_test.prototxt配置文件注解
- sql/plus无法显示数据库问题
- xxx while the managed IDbConnection interface was being used: Login failed for user xxx