平衡树

平衡树

普通平衡树

加强版

vector伪平衡树

数据范围最大是\(10^5\) ,毕竟复杂度是 \(O(n^2 )\)

无O2时千万不要使用

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
vector <int> v;
int n;
int main(){
    n=read();
    for(int i=1,opt,x;i<=n;i++){
        opt=read();x=read();
        if(opt==1) v.insert(lower_bound(v.begin(),v.end(),x),x);
        if(opt==2)  v.erase(lower_bound(v.begin(),v.end(),x));
        if(opt==3)  printf("%d\n",lower_bound(v.begin(),v.end(),x)-v.begin()+1);
        if(opt==4)  printf("%d\n",v[x-1]);
        if(opt==5)  printf("%d\n",*--lower_bound(v.begin(),v.end(),x));
        if(opt==6)  printf("%d\n",*upper_bound(v.begin(),v.end(),x));
    }
    return 0;
}

vector套vector

  1. 把初始序列划分成几段,每段大小长度为800(您也可以试试别的数,比如1000或500)
  2. 显然如果它一直往一个块里插入就会TLE,于是我们引入 分裂(split)与合并(merge)
  3. 分裂: 如果一个块大小超过2*S,就分成两块
  4. 合并: 如果一个块大小小于S/2.就与两边的较小块合并
  5. 分裂与合并可以使用insert,而不是把一个一个元素拿出来再push_back
  6. 1,2,3,5,6操作就是二分找到块,然后二分找到元素所在点进行统计(修改)
  7. 4操作直接从第一个块往后遍历,每过一个块就减去它的大小,直到不能减(找到了)
  8. 每次操作如果修改了块,就判断是否进行分裂与修改操作
  9. 复杂度分析: 小常数\(O(n \sqrt n)\),不过跑的非常快(常数极小)
  10. 注意: 上述 nn 均指数据范围 n+m\

01trie伪平衡树

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int Q=1e7;
struct TREE{
    int ch[2],size,end;
};
struct Trie{
    TREE t[400005];
    int tot;

    Trie(){memset(t,0,sizeof(t));tot=1;}

    void insert(int x,int val){
        int p=1;
        for(int i=30;i>=0;i--){
            int f=(x>>i)&1;
            if(!t[p].ch[f]) t[p].ch[f]=++tot;
            p=t[p].ch[f];t[p].size+=val;
        }
        t[p].end+=val;
    }
    int get_sum(int x){
        int p=1;
        for(int i=30;i>=0;i--){
            int f=(x>>i)&1;
            p=t[p].ch[f];
            if(!p) return 0;
        }
        return t[p].end;
    }
    int get_rank(int x){
        int p=1,res=0;
        for(int i=30;i>=0;i--){
            int f=(x>>i)&1;
            if(f) res+=t[t[p].ch[0]].size;
            p=t[p].ch[f];
        }
        return res+1;
    }
    int get_kth(int k){
        int p=1,res=0;
        for(int i=30;i>=0;i--){
            int x=t[t[p].ch[0]].size;
            if(k<=x) p=t[p].ch[0];
            else {
                k-=x;
                p=t[p].ch[1];
                res+=(1<<i);
            }
        }
        return res;
    }
    int get_pre(int x){
        return get_kth(get_rank(x)-1);
    }
    int get_nxt(int x){
        return get_kth(get_rank(x)+get_sum(x));
    }
}T;
int n;
int main(){
    scanf("%d", &n); 
    for(int i=1,opt,x;i<=n;i++){
        scanf("%d%d",&opt,&x); 
        if(opt==1) T.insert(x+Q,1);
        if(opt==2) T.insert(x+Q,-1);
        if(opt==3) printf("%d\n",T.get_rank(x+Q));
        if(opt==4) printf("%d\n",T.get_kth(x)-Q);
        if(opt==5) printf("%d\n",T.get_pre(x+Q)-Q);
        if(opt==6) printf("%d\n",T.get_nxt(x+Q)-Q);
    }
    return 0;
}

权值线段树

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=4e5+10;
const int INF=0x3f3f3f3f;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}

#define mid ((l+r)>>1)
int rt,tot,ls[N],rs[N],cnt[N];
void add(int l,int r,int k,int v,int &p) {
	if(!p) p=++tot;
	if(l==r) {
		cnt[p]+=v;
		return;
	}
	if(k<=mid) add(l,mid,k,v,ls[p]);
	else add(mid+1,r,k,v,rs[p]);
	cnt[p]=cnt[ls[p]]+cnt[rs[p]];
}

//求权值区间元素个数 
int query(int l,int r,int L,int R,int p) {
	if(!p) return 0;
	if(L<=l&&r<=R) return cnt[p];
	int res=0;
	if(L<=mid) res+=query(l,mid,L,R,ls[p]);
	if(R>mid) res+=query(mid+1,r,L,R,rs[p]);
	return res;
}
	
int kth(int l,int r,int x,int p) {
	if(l==r) return l;
	if(cnt[ls[p]]>=x) return kth(l,mid,x,ls[p]);
	else return kth(mid+1,r,x-cnt[ls[p]],rs[p]);
}

int get_rk(int l,int r,int k,int p) {
	if(!p) return 0;
	if(l==r) return 1;
	if(k<=mid) return get_rk(l,mid,k,ls[p]);
	else return cnt[ls[p]]+get_rk(mid+1,r,k,rs[p]);
}  

int get_pre(int x) {
	int v=query(-INF,INF,-INF,x-1,rt);
	return kth(-INF,INF,v,rt);
}

int get_nxt(int x) {
	int v=query(-INF,INF,-INF,x,rt)+1;
	return kth(-INF,INF,v,rt);
}

int main() {
	int n=read();
	int op,x;
	while(n--) {
		op=read();x=read();
		switch(op) {
			case 1 : add(-INF,INF,x,1,rt);break;
			case 2 : add(-INF,INF,x,-1,rt);break;
			case 3 : printf("%d\n",get_rk(-INF,INF,x,1));break;
			case 4 : printf("%d\n",kth(-INF,INF,x,1));break;
			case 5 : printf("%d\n",get_pre(x));break;
			case 6 : printf("%d\n",get_nxt(x));break;
		}
	} 
	return 0;
}

权值树状数组(快而简单)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int M=1e7+10;
const int N=1e5+10;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
int n;
int c[M],tot;
int op[N],x[N],b[N];
inline int get(int x) {
	return lower_bound(b+1,b+1+tot,x)-b;
}
inline void upd(int x,int v) {for(;x<=tot;x+=x&(-x)) c[x]+=v;} 
inline int sum(int x) {int res=0;for(;x;x-=x&(-x)) res+=c[x];return res;} 
inline int query(int x) {
	int t=0;
	for(int i=19;i>=0;i--) {
		t+=(1<<i);
		if(t>tot||c[t]>=x) t-=(1<<i);//注意等号 
		else x-=c[t];
	}
	return b[t+1];
}
int main() {
	n=read();
	for(int i=1;i<=n;i++) {
		op[i]=read();x[i]=read();
		if(op[i]!=4) b[++tot]=x[i];
	}
	sort(b+1,b+1+tot);
	tot=unique(b+1,b+1+n)-b-1;
	
	for(int i=1;i<=n;i++) {
		switch(op[i]) {
			case 1 : upd(get(x[i]),1);break;
			case 2 : upd(get(x[i]),-1);break;
			case 3 : printf("%d\n",sum(get(x[i])-1)+1);break;
			case 4 : printf("%d\n",query(x[i]));break;
			case 5 : printf("%d\n",query(sum(get(x[i])-1)));break;
			case 6 : printf("%d\n",query(sum(get(x[i]))+1));break;
		}
	}
	return 0;
}

啊切入正题


平衡树有好多。。。BST, Treap ,Splay ...

ftq——treap

学长强推 fhq 防火墙 treap(也叫无旋treap)

无旋Treap有两种基本操作:

(1)分裂Split

 split有两种:一种是按照权值split,一种是按照size来split。

 如果按照权值split,那么分出来两棵树的第一棵树上的每一个数的大小都小于(或小于等于)x;

 如果按照size split,那么分出来两棵树的第一棵树恰好有x个节点。

 我们可以结合具体代码讲解: 

(2)合并Merge

将两棵树合为一棵树,其中树A的最大点权小等于树B的最小点权.为了保证树高期望为log,我们要想办法随机合并.

两种合并方法是等价的.都是把其中一个节点当作这一步的根节点,另一个节点和根节点的子树递归合并.请再次回忆二叉搜索树的性质,所以我们一定要保证A在B的"左边".

一定要注意啊,Merge(A,B)和Merge(B,A)天差地别啊!

常规操作

查排名,查前驱后继等操作同普通平衡树,在树上dfs即可.不过为了体现无旋Treap的优越,下方给出的实例代码是利用两种基本操作实现的,优点在于直观,好写,缺点在于比起直接dfs的话常数略大.

插入节点

新建一个节点,然后把树分为x,y两部分,然后把新的节点a看做是一棵树,先与x合并,合并完之后将合并的整体与y合并

删除节点

首先我们把树分为x和z两部分

那么x树中的最大权值为a

再把x分为x和y两部分。

此时x中的最大权值为a-1,且权值为a的节点一定是y的根节点。

然后我们可以无视y的根节点,直接把y的左右孩子合并起来,这样就成功的删除了根节点,

最后再把x,y,z合并起来就好

查询x的排名

我们首先按照a-1的权值把树分开。

那么x树中最大的应该是a-1。

那么a的排名就是siz[x]+1

查询排名为a的数

额直接调用函数,,,函数下面自己看

求x的前驱(前驱定义为小于a,且最大的数)

因为要小于a,那么我们按照a-1的权值划分,

x中最大的一定是<=a-1的,

所以我们直接输出x中最大的数就好,

(这里有一个小技巧,因为siz储存的是节点的数目,然后根据二叉查找树的性质,编号最大的就是值最大的)

区间操作

一般来讲是通过Split剖出你需要操作的区间代表的子树,然后在根节点打标记,然后合并即可.

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
using namespace std;
const int N = 1e5+10;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,tree_cnt,root;
struct fhq_tree{
    int ch[2],siz,val,rnd;
}t[N];
inline void pushup(int p) {
    t[p].siz=t[t[p].ch[0]].siz+t[t[p].ch[1]].siz+1;
}//注意和线段树不同,加上1(自己)
int insert(int x) {
    t[++tree_cnt].siz=1;t[tree_cnt].val=x;t[tree_cnt].rnd=rand();
    return tree_cnt;//记得返回
}
inline int merge(int x,int y) {
    if(!x||!y) return x|y;
    if(t[x].rnd<t[y].rnd) {
        t[x].ch[1]=merge(t[x].ch[1],y);
        pushup(x);
        return x;
    } else {
        t[y].ch[0]=merge(x,t[y].ch[0]);
        pushup(y);
        return y;
    }
}
inline void split(int p,int k,int &x,int &y) {
    if(!p) {x=y=0;return;}
    if(t[p].val<=k) {
        x=p;
        split(t[p].ch[1],k,t[p].ch[1],y);
    } else {
        y=p;
        split(t[p].ch[0],k,x,t[p].ch[0]);
    }
    pushup(p);
}
int kth(int p,int k) {
    while(1) {
        if(k<=t[t[p].ch[0]].siz) p=t[p].ch[0];
        else {
            if(k==t[t[p].ch[0]].siz+1) return p;
            else {
                k-=t[t[p].ch[0]].siz+1;
                p=t[p].ch[1];
            }
        }
    }
}

int main(){
    srand(time(0));
    n=read();
    int x,y,z,u,opt;
    for(int i=1;i<=n;i++) {
        opt=read();u=read();
        if(opt==1) {
            split(root,u,x,y);
            root=merge(merge(x,insert(u)),y);
        } else if(opt==2) {
            split(root,u,x,z);//把x完全分出去
            split(x,u-1,x,y);
            y=merge(t[y].ch[0],t[y].ch[1]);
            root=merge(merge(x,y),z);
        } else if(opt==3) {
            split(root,u-1,x,y);
            printf("%d\n",t[x].siz+1);
            root=merge(x,y);
        } else if(opt==4) {
            printf("%d\n",t[kth(root,u)].val);
        } else if(opt==5) {
            split(root,u-1,x,y);
            printf("%d\n",t[kth(x,t[x].siz)].val);
            root=merge(x,y);
        } else {
            split(root,u,x,y);
            printf("%d\n",t[kth(y,1)].val);
            root=merge(x,y);
        }
    }
    return 0;
}


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int N = 2e5+10;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,tree_cnt,root,seed=1;
int x,y,z,u,opt;
struct fhq_tree{
    int ls,rs,siz,val,rnd;
}t[N];
void pushup(int p) {
    t[p].siz=t[t[p].ls].siz+t[t[p].rs].siz+1;
}//注意和线段树不同,加上1(自己)
inline int rand_(void) { return seed *= 482711; }
int merge(int x,int y) {
    if(!x||!y) return x|y;
    if(t[x].rnd<t[y].rnd) {
        t[x].rs=merge(t[x].rs,y);
        pushup(x);
        return x;
    } else {
        t[y].ls=merge(x,t[y].ls);
        pushup(y);
        return y;
    }
}
void split(int p,int k,int &x,int &y) {
    if(!p) {x=y=0;return;}
    if(t[p].val<=k) {
        x=p;
        split(t[p].rs,k,t[p].rs,y);
    } else {
        y=p;
        split(t[p].ls,k,x,t[p].ls);
    }
    pushup(p);
}
int insert(int x) {
    t[++tree_cnt].siz=1;t[tree_cnt].val=x;t[tree_cnt].rnd=rand_();
    split(root,u,x,y);
    root=merge(merge(x,tree_cnt),y);
}
int kth(int p,int k) {
    while(k!=t[t[p].ls].siz+1)
        if(k<=t[t[p].ls].siz) p=t[p].ls;
        else k-=t[t[p].ls].siz+1,p=t[p].rs;
    return p;
}

int main(){
    freopen("3.in","r",stdin);
    // srand(time(0));
    n=read();m=read();
    for(int i=1;i<=n;i++) {
        u=read();
        insert(u);
    }
    int last=0,ans=0;
    for(int i=1;i<=m;i++) {
        opt=read();u=read()^last;
        if(opt==1) {
            insert(u);
        } else if(opt==2) {
            split(root,u,x,z);//把x完全分出去
            split(x,u-1,x,y);
            y=merge(t[y].ls,t[y].rs);
            root=merge(merge(x,y),z);
        } else if(opt==3) {
            split(root,u-1,x,y);
            last=t[x].siz+1;
            ans^=last;
            root=merge(x,y);
        } else if(opt==4) {
            last=t[kth(root,u)].val;
            ans^=last;
        } else if(opt==5) {
            split(root,u-1,x,y);
            last=t[kth(x,t[x].siz)].val;
            ans^=last;
            root=merge(x,y);
        } else {
            split(root,u,x,y);
            last=t[kth(y,1)].val;
            ans^=last;
            root=merge(x,y);
        }
    }
    printf("%d\n",ans);
    return 0;
}

文艺平衡树——区间翻转

fhq

#include <iostream>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <cstdlib>
using namespace std;
const int N=1000010;
int n,m,tot,root;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
struct fhq_treap{
    int siz[N],val[N],rnd[N],ls[N],rs[N];
    bool tag[N];
    void pushup(int p) {
        siz[p]=siz[ls[p]]+siz[rs[p]]+1;
    }
    int insert(int x) {
        siz[++tot]=1;val[tot]=x;rnd[tot]=rand();
        return tot;
    }
    void down(int x) {
        swap(ls[x],rs[x]);
        if(ls[x]) tag[ls[x]]^=1;
        if(rs[x]) tag[rs[x]]^=1;
        tag[x]=0;
    }
    int merge(int x,int y) {
        if(!x||!y) return x|y;
        if(rnd[x]<rnd[y]) {
            if(tag[x]) down(x);
            rs[x]=merge(rs[x],y);
            pushup(x); return x;
        } else {
            if(tag[y]) down(y);
            ls[y]=merge(x,ls[y]);   
            pushup(y); return y;
        }
    }
    void split(int p,int k,int &x,int &y) {
        if(!p) {x=y=0;return;}
        if(tag[p]) down(p);
        if(siz[ls[p]]<k) {
            x=p;split(rs[p],k-siz[ls[p]]-1,rs[p],y);
        } else {
            y=p;split(ls[p],k,x,ls[p]);
        }
        pushup(p);
    }
    void print(int p) {
        if(!p) return;
        if(tag[p]) down(p);
        print(ls[p]);
        printf("%d ",val[p]);
        print(rs[p]);
    }
}T;
int main() {
    srand(time(0));
    n=read();m=read();
    for(int i=1;i<=n;i++)  
        root=T.merge(root,T.insert(i));
    for(int i=1;i<=m;i++) {
		int l,r,a,b,c;
        l=read();r=read();
		T.split(root,l-1,a,b);
		T.split(b,r-l+1,b,c);
		T.tag[b]^=1;
		root=T.merge(a,T.merge(b,c));
	}
    T.print(root);
    return 0;
}
posted @ 2020-10-04 20:01  ke_xin  阅读(28)  评论(0编辑  收藏  举报