• 题解 P3369 【【模板】普通平衡树】


    在网上某篇神奇的教程和@codesonic 大佬的标程帮助下,我又肝完了Leafy Tree,跑过来写篇题解(好像以前写过一篇?)


    什么是Leafy Tree?

    Leafy Tree由两种节点组成:辅助节点与叶子节点。

    叶子节点储存值,而辅助节点储存左右孩子中大的那个值。

    注意:辅助节点必定有两个孩子。

    操作如何实现?

    拿插入操作举例:

    一路向下递归,每次拿左子树最大值与插入值作比较,如果大就往左,如果小就往右。

    到底了就插入叶子与辅助。

    然后再回溯更新。

    这时候就会出一个问题:这个算法很容易被数据卡。

    解决方案是引入平衡因子,在适当的时候重建这颗树。

    重构方法可以拍扁也可以旋转。

    拍扁的方法就是中序遍历一遍然后重新建树(具体可以参考这里),旋转的一会儿会讲。


    工具函数

    这里是一些简单的重要的工具函数。

    1. 新建节点
    inline void newNode(int &pos,int v){
    	pos=++cnt,size[pos]=1,val[pos]=v;
    }
    

    cnt是总结点个数,size是子树大小,val是值(废话)。

    1. 复制节点
    inline void copyNode(int x,int y){
    	size[x]=size[y],ls[x]=ls[y],rs[x]=rs[y],val[x]=val[y];
    }
    

    没什么好说的

    1. 合并节点
    void merge(int l,int r){
    	size[++cnt]=size[l]+size[r],val[cnt]=val[r],ls[cnt]=l,rs[cnt]=r;
    }
    
    1. 旋转
    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的左旋与右旋。

    1. 重建
    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
    ",rank(root,a));
            if(s==4)printf("%d
    ",kth(root,a));
            if(s==5)printf("%d
    ",kth(root,rank(root,a)-1));
            if(s==6)printf("%d
    ",kth(root,rank(root,a+1)));
        }
    }
    
  • 相关阅读:
    idea git 操作
    1
    python 迭代器/生成器/迭代对象
    python 中的type
    systemd 配置文件
    python 中类的初始化过程
    mysql主从错误180301
    从零开始搭建k8s-20180301
    kubernetes role
    Java程序员毕业两年自述
  • 原文地址:https://www.cnblogs.com/ilverene/p/9850564.html
Copyright © 2020-2023  润新知