zoukankan      html  css  js  c++  java
  • P3369 【模板】普通平衡树

    splay

    splay与他的解析

    二叉搜索树

    我们搞一棵树,保证左子树所有点的权值比父亲小,右子树所有点权值比父亲大

    显然这个玩意可以快速查询一个数存不存在,排名啦啥的

    插入的时候直接顺序造节点,删除的时候,断开重连是件很愚蠢的事情

    应该把删除节点和他右子树最左边那个或者左子树最右边那个交换,销毁它

    这样有什么问题呢?

    如果坑逼出题人给你搞成了链,直接起飞成$ O(n)$了

    这不就挂了

    那么该怎么搞呢

    伸展树splay出现了

    splay,旋转,自我调整,

    旋转

    捕获.PNG

    本图片引用自liuzhangfeiabc课件

    我们可以看到,转x,就是让f变成x的右儿子,x的右儿子变成f的左儿子,x的右儿子变成f,x代替f

    void ro(int x){
    	int f=po[x].fa;
    	int gf=po[f].fa;
    	int xp=ident(x);
    	int fp=ident(f);
    	connect(po[x].son[xp^1],f,xp);
    	connect(f,x,xp^1);
    	connect(x,gf,fp);
    	update(f);
    	update(x);
    }
    

    但是出题人还能接着卡你

    怎么办呢,我们一次转俩,直接强行链改树

    splay

    void splay(int x,int to){
    	to=po[to].fa;
    	while(po[x].fa!=to){
    		int f=po[x].fa;
    		if(po[f].fa==to) ro(x);
    		else if(ident(x)==ident(f)) ro(f),ro(x);
    		else ro(x),ro(x);
    	}
    }.
    

    这又是在干什么呢

    我们可以把一个节点移到你想去的任何位置如果爹,爹的爹和儿子共线,就要先转爹,不然就转两次儿子,就可以成功的压成树了

    更具体的操作

    插入 这里没什么大坑,就是记得最后splay一下

    void insert(int x){
    	int now=po[0].son[1];
    	if(now==0){
    		creat(x,0);
    		po[0].son[1]=tot;
    	}else{
    		while(1){
    			po[now].sum++;
    			if(po[now].val==x){
    				po[now].cnt++;
    				splay(now,po[0].son[1]);
    				return ;
    			}
    			int nex=x<po[now].val?0:1;
    			if(!po[now].son[nex]){
    				int p=creat(x,now);
    				po[now].son[nex]=p;
    				splay(p,po[0].son[1]);
    				return ;
    			}
    			now=po[now].son[nex];
    		}
    	}
    }
    

    寻找排名

    也很好理解,记得splay

    int find(int x){
    	int now=po[0].son[1];
    	while(1){
    		if(!now) return 0;
    		if(po[now].val==x) {
    			splay(now,po[0].son[1]);
    			return now;
    		}
    		int nex=x<po[now].val?0:1;
    		now=po[now].son[nex];
    	}
    }
    

    删除

    还是很好解决的,先找到它,如果它是叶子,直接删掉,如果它没有左儿子的,就直接把右儿子接上去,如果都有,就找它右子树最左的那个,splay进行交换,销毁

    void del(int x){
    	int pos=find(x);
    	if(!pos) return ;
    	if(po[pos].cnt>1){
    		po[pos].cnt--;
    		po[pos].sum--;
    		return ;
    	}else{
    		if(!po[pos].son[0]&&!po[pos].son[1]) {
    			po[0].son[1]=0;
    			return ;
    		}else if(!po[pos].son[0]){
    			po[0].son[1]=po[pos].son[1];
    			po[po[0].son[1]].fa=0;
    			return ;
    		} else{
    			int le=po[pos].son[0];
    			while(po[le].son[1]) le=po[le].son[1];
    			splay(le,po[pos].son[0]);
    			connect(po[pos].son[1],le,1);
    			connect(le,0,1);
    			update(le);
    		}
    	}
    }
    

    按照排名找数

    同理

    找前缀

    记得一直取max

    后缀

    和前缀反着

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cstring>
    #define int long long
    using namespace std;
    const int maxn=1e6+10;
    const int mod=10007;
    const int inf=1e9+10;
    struct p{
    	int fa;
    	int son[2];
    	int val;
    	int cnt;
    	int sum;
    }po[maxn];
    int tot;
    int n,opt;int x;
    int ps;
    inline int read(){
        int res=0,k=1;
        char c=getchar();
        while(!isdigit(c)){
            if(c=='-')k=-1;
            c=getchar();
        }
        while(isdigit(c)){
            res=(res<<1)+(res<<3)+c-48;
            c=getchar();
        }
        return res*k;
    }
    void update(int x){
    	po[x].sum=po[po[x].son[0]].sum+po[po[x].son[1]].sum+po[x].cnt;
    }
    int ident(int x){
    	return po[po[x].fa].son[0]==x?0:1;
    }
    void connect(int x,int fa,int s){
    	po[fa].son[s]=x;
    	po[x].fa=fa;
    }
    void ro(int x){
    	int f=po[x].fa;
    	int gf=po[f].fa;
    	int xp=ident(x);
    	int fp=ident(f);
    	connect(po[x].son[xp^1],f,xp);
    	connect(f,x,xp^1);
    	connect(x,gf,fp);
    	update(f);
    	update(x);
    }
    void splay(int x,int to){
    	to=po[to].fa;
    	while(po[x].fa!=to){
    		int f=po[x].fa;
    		if(po[f].fa==to) ro(x);
    		else if(ident(x)==ident(f)) ro(f),ro(x);
    		else ro(x),ro(x);
    	}
    }
    int creat(int val,int f){
    	++tot;
    	po[tot].fa=f;
    	po[tot].sum=po[tot].cnt=1;
    	po[tot].val=val;
    	return tot;
    }
    void insert(int x){
    	int now=po[0].son[1];
    	if(now==0){
    		creat(x,0);
    		po[0].son[1]=tot;
    	}else{
    		while(1){
    			po[now].sum++;
    			if(po[now].val==x){
    				po[now].cnt++;
    				splay(now,po[0].son[1]);
    				return ;
    			}
    			int nex=x<po[now].val?0:1;
    			if(!po[now].son[nex]){
    				int p=creat(x,now);
    				po[now].son[nex]=p;
    				splay(p,po[0].son[1]);
    				return ;
    			}
    			now=po[now].son[nex];
    		}
    	}
    }
    int find(int x){
    	int now=po[0].son[1];
    	while(1){
    		if(!now) return 0;
    		if(po[now].val==x) {
    			splay(now,po[0].son[1]);
    			return now;
    		}
    		int nex=x<po[now].val?0:1;
    		now=po[now].son[nex];
    	}
    }
    void del(int x){
    	int pos=find(x);
    	if(!pos) return ;
    	if(po[pos].cnt>1){
    		po[pos].cnt--;
    		po[pos].sum--;
    		return ;
    	}else{
    		if(!po[pos].son[0]&&!po[pos].son[1]) {
    			po[0].son[1]=0;
    			return ;
    		}else if(!po[pos].son[0]){
    			po[0].son[1]=po[pos].son[1];
    			po[po[0].son[1]].fa=0;
    			return ;
    		} else{
    			int le=po[pos].son[0];
    			while(po[le].son[1]) le=po[le].son[1];
    			splay(le,po[pos].son[0]);
    			connect(po[pos].son[1],le,1);
    			connect(le,0,1);
    			update(le);
    		}
    	}
    }
    int rak(int x){
    	int now=po[0].son[1];
    	int ans=0;
    	while(1){
    		if(po[now].val==x) {
    	//	splay(now,po[0].son[1]);
    		 ans+=po[po[now].son[0]].sum+1;
    		 splay(now,po[0].son[1]);;
    		 return ans;
    		}
    		int nex=x<po[now].val?0:1;
    		if(nex==1) ans =ans+po[po[now].son[0]].sum+po[now].cnt;
    		now=po[now].son[nex];
    	}
    }
    int kth(int x){
    	int now=po[0].son[1];
    	while(1){
    		int re=po[now].sum-po[po[now].son[1]].sum;
    		if(po[po[now].son[0]].sum<x&&x<=re){
    			return po[now].val;
    		}
    		if(x<re) now=po[now].son[0];
    		else now =po[now].son[1],x-=re;
    	}
    }
    int lower(int x){
    	int now=po[0].son[1];
    	int ans=-inf;
    	while(now){
    		if(po[now].val<x) ans=max(ans,po[now].val);
    		int nex=x<=po[now].val?0:1;//等于也要往左走 
    		now=po[now].son[nex];
    	}
    	return ans;
    }
    int upper(int x){
    	int now=po[0].son[1];
    	int ans=inf;
    	while(now){
    		if(po[now].val>x) ans=min(ans,po[now].val);
    		int nex=x<po[now].val?0:1;
    		now=po[now].son[nex];
    	}
    	return ans;
    }
    signed main(){
    	n=read();
    	while(n--){
    		opt=read();
    		x=read();
    		if(opt==1) insert(x);
            else if(opt==2) del(x);
            else if(opt==3) printf("%d
    ",rak(x));
            else if(opt==4) printf("%d
    ",kth(x));
            else if(opt==5) printf("%d
    ",lower(x));
            else if(opt==6) printf("%d
    ",upper(x));
    	}
    	return 0;
    }
    
  • 相关阅读:
    27. Remove Element
    列表变成字典
    1. Two Sum
    CVPR2019:What and How Well You Performed? A Multitask Learning Approach to Action Quality Assessment
    959. Regions Cut By Slashes
    118. Pascal's Triangle
    loj3117 IOI2017 接线 wiring 题解
    题解 NOI2019 序列
    题解 省选联考2020 组合数问题
    题解 Educational Codeforces Round 90 (Rated for Div. 2) (CF1373)
  • 原文地址:https://www.cnblogs.com/For-Miku/p/15030626.html
Copyright © 2011-2022 走看看