zoukankan      html  css  js  c++  java
  • 关于非旋转treap的学习

    非旋转treap的操作基于split和merge操作,其余操作和普通平衡树一样,复杂度保证方式与旋转treap差不多,都是基于一个随机的参数,这样构出的树树高为(logn)

    split

    作用:将原平衡树分为排名为([1,k]),([k+1,n])的两棵平衡树
    实现:
    1.如果(x)左儿子的子树大小(size[l]==k),那么(x)左儿子即为([1,k])这个平衡树的根,(x)本身为另一颗平衡树的根
    2.如果(x)左儿子的子树大小(size[l]+1==k),那么(x)([1,k])这个平衡树的根,(x)右儿子为另一颗平衡树的根
    3.如果是上述情况,直接把左儿子或右儿子接上去就好
    4.否则,递归处理,直到出现这两种情况位置

    inline void split(int x,int k,int &a,int &b){
    	if(!k){a=0;b=x;return ;}
    	int l=ch[x][0],r=ch[x][1];
    	if(sz[l]==k)ch[x][0]=0,a=l,b=x;
    	else if(sz[l]+1==k)ch[x][1]=0,a=x,b=r;
    	else if(sz[l]>k)split(l,k,a,ch[x][0]),b=x;
    	else split(r,k-sz[l]-1,ch[x][1],b),a=x;
    	upd(x);
    }
    

    merge

    作用:将两棵平衡树合并,合并后参数满足(heap)的性质,且满足平衡树性质.
    实现:
    我们强制(merge(x,y))(x)的所有节点的关键字都小于所有(y)的关键字,根据关键字合并即可.

    inline int merge(int x,int y){
    	if(!x||!y)return x+y;
    	if(key[x]>key[y]){
    		ch[x][1]=merge(ch[x][1],y);
    		upd(x);
    		return x;
    	}
    	else{
    		ch[y][0]=merge(x,ch[y][0]);
    		upd(y);
    		return y;
    	}
    }
    

    insert

    (x)的前驱为界,拆原树为两棵树,将左树合并到(x),再将(x)合并到右树

    inline void ins(int x){
    	v[++cnt]=x;key[cnt]=rand();sz[cnt]=1;
    	int l,r;
    	split(rt,rank(x)-1,l,r);
    	rt=merge(merge(l,cnt),r);
    }
    

    delet

    (x)为界(split)原树,删除(x)后再合并左右子树

    inline void Delet(int x){
    	int l,r;
    	split(rt,x,l,r);
    	split(l,x-1,l,x);
    	rt=merge(l,r);
    }
    

    标记下放

    只需在(merge),(split),(kth),(rank)函数前下放即可

    完整代码:

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=100005;
    int ch[N][2],sz[N],key[N],cnt=0,v[N],rt;
    inline void upd(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1;}
    inline void split(int x,int k,int &a,int &b){
    	if(!k){a=0;b=x;return ;}
    	int l=ch[x][0],r=ch[x][1];
    	if(sz[l]==k)ch[x][0]=0,a=l,b=x;
    	else if(sz[l]+1==k)ch[x][1]=0,a=x,b=r;
    	else if(sz[l]>k)split(l,k,a,ch[x][0]),b=x;
    	else split(r,k-sz[l]-1,ch[x][1],b),a=x;
    	upd(x);
    }
    inline int merge(int x,int y){
    	if(!x||!y)return x+y;
    	if(key[x]>key[y]){
    		ch[x][1]=merge(ch[x][1],y);
    		upd(x);
    		return x;
    	}
    	else{
    		ch[y][0]=merge(x,ch[y][0]);
    		upd(y);
    		return y;
    	}
    }
    inline int rank(int k){
    	int x=rt,ret=1;
    	while(x){
    		if(k<=v[x])x=ch[x][0];
    		else ret+=sz[ch[x][0]]+1,x=ch[x][1];
    	}
    	return ret;
    }
    inline int kth(int k){
    	int x=rt,sum;
    	while(x){
    		sum=sz[ch[x][0]];
    		if(sum==k-1)return v[x];
    		if(sum>=k)x=ch[x][0];
    		else x=ch[x][1],k-=sum+1;
    	}return 0;
    }
    inline void ins(int x){
    	v[++cnt]=x;key[cnt]=rand();sz[cnt]=1;
    	int l,r;
    	split(rt,rank(x)-1,l,r);
    	rt=merge(merge(l,cnt),r);
    }
    inline void Delet(int x){
    	int l,r;
    	split(rt,x,l,r);
    	split(l,x-1,l,x);
    	rt=merge(l,r);
    }
    void work()
    {
    	int n,op,x;
    	scanf("%d",&n);
    	while(n--){
    		scanf("%d%d",&op,&x);
    		if(op==1)ins(x);
    		else if(op==2)Delet(rank(x));
    		else if(op==3)printf("%d
    ",rank(x));
    		else if(op==4)printf("%d
    ",kth(x));
    		else if(op==5)printf("%d
    ",kth(rank(x)-1));
    		else if(op==6)printf("%d
    ",kth(rank(x+1)));
    	}
    }
    
    int main()
    {
    	freopen("pp.in","r",stdin);
    	freopen("pp.out","w",stdout);
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    NHibernate初学二之简单执行SQL及HQL、Linq
    Socket通信常用方法
    Android中自定义属性的使用
    四种常见的 POST 提交数据方式
    关于 Content-Type:application/x-www-form-urlencoded 和 Content-Type:multipart/related
    1、memcache的守护进程启动方式(2017-8-10)
    c++封装简单日志操作
    关于vector的内存释放
    Linux进程通信-共享内存
    Linux进程通信总结
  • 原文地址:https://www.cnblogs.com/Yuzao/p/8094551.html
Copyright © 2011-2022 走看看