suxxsfe

一言(ヒトコト)

替罪羊树

一般的平衡树都是基于旋转来保证树的平衡,也就是让树不会太高,但替罪羊树是用暴力重构来保证
具体的,就是插入时如果一个节点的左或右子树,节点数大于根的节点数乘一个平衡因子 alpha,那么就重构以这个节点为根的整个子树

inline int isbad(tr *tree){
	return tree->ls->cnt>alpha*tree->cnt+5||tree->rs->cnt>alpha*tree->cnt+5;
}

这个加五,是为了防止在子树较小的时候造成多次重构

但是,如果某一次插入时根节点到插入节点的路径上,有好多节点都满足这个条件,那会造成多次重构
这显然是十分浪费的,所以用一个 badtag 记录下最靠上的一个满足条件的点,然后只重构一次

重构过程就是先来一遍 dfs(中序遍历),把所以节点存在一个指针数组里,然后指针数组里节点的值肯定是递增的,那么重构的递归里,就把当前下标范围 \((l,r)\)\(mid\) 作为节点,然后递归的重构 \((l,mid-1),(mid+1,r)\)

至于删除,是惰性删除,给要删的节点打一个删除标记,然后重构的时候把它删掉
所以保存的大小一个是确实存在的节点数(不包含已经删了的),和节点总数(包含已经删了的)。显然, isbad 比较的是节点总数,因为节点总数反映的才是子树的重量(或者说高度)

剩余的就和普通的二叉搜索树差不多了,注意如果有多个相同的值,一个节点只代表一个
可以说代码是非常好理解

https://www.luogu.com.cn/problem/P6136
https://www.luogu.com.cn/problem/P3369

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define alpha 0.7
struct tr{
	tr *ls,*rs;
	int val,cnt,size;//size:num of not deleted,cnt:all
	int deleted;
}*null,*root,*nodes[100005],**badtag;
int node_num;
inline int isbad(tr *tree){
	return tree->ls->cnt>alpha*tree->cnt+5||tree->rs->cnt>alpha*tree->cnt+5;
}
void dfs(tr *tree){
	if(tree==null) return;
	dfs(tree->ls);
	if(!tree->deleted) nodes[++node_num]=tree;
	dfs(tree->rs);
	if(tree->deleted) delete tree;
}
tr *build(int l,int r){
	if(l>r) return null;
	if(l==r){
		nodes[l]->ls=nodes[l]->rs=null;
		nodes[l]->cnt=nodes[l]->size=1;
		return nodes[l];
	}
	int mid=(l+r)>>1;
	tr *tree=nodes[mid];
	tree->ls=build(l,mid-1);tree->rs=build(mid+1,r);
	tree->cnt=tree->size=1+tree->ls->size+tree->rs->size;
	return tree;
}
inline void rebuild(tr *&tree){
	node_num=0;
	dfs(tree);
//		std::printf("size : %d\n",node_num-1);
	tree=build(1,node_num);
}
void insert(tr *&tree,int x){
	if(tree==null){
		tree=new tr;
		tree->ls=tree->rs=null;
		tree->deleted=0;tree->val=x;
		tree->size=tree->cnt=1;
		return;
	}
	tree->size++;tree->cnt++;
	if(x>tree->val) insert(tree->rs,x);
	else insert(tree->ls,x);
	if(isbad(tree)) badtag=&tree;
}
inline void insert_(tr *&tree,int x){
	badtag=&null;
	insert(tree,x);
	if(badtag!=&null) rebuild(*badtag);
}
inline int rank(tr *tree,int x){
	reg int ans=1;
	while(tree!=null){
		if(x<=tree->val) tree=tree->ls;
		else{
			ans+=tree->ls->size+!tree->deleted;
			tree=tree->rs;
		}
	}
	return ans;
}
inline int kth(tr *tree,int rk){
	while(tree!=null){
		if(!tree->deleted&&tree->ls->size+1==rk) return tree->val;
		if(rk<=tree->ls->size) tree=tree->ls;
		else{
			rk-=tree->ls->size+!tree->deleted;
			tree=tree->rs;
		}
	}
}
void erase(tr *tree,int rk){
	if(!tree->deleted&&rk==tree->ls->size+1){
		tree->deleted=1;
		tree->size--;
		return;
	}
	tree->size--;
	if(rk<=tree->ls->size+!tree->deleted) erase(tree->ls,rk);
	else erase(tree->rs,rk-tree->ls->size-!tree->deleted);
}
int main(){
	int n=read();
	null=new tr;
	null->ls=null->rs=NULL;
	null->deleted=null->cnt=null->size=null->val=0;
	root=null;
	reg int op,x;
	while(n--){
		op=read();x=read();
		if(op==1) insert_(root,x);
		else if(op==2) erase(root,rank(root,x));
		else if(op==3) printf("%d\n",rank(root,x));
		else if(op==4) printf("%d\n",kth(root,x));
		else if(op==5) printf("%d\n",kth(root,rank(root,x)-1));
		else printf("%d\n",kth(root,rank(root,x+1)));
	}
	return 0;
}
posted @ 2020-07-28 18:51  suxxsfe  阅读(184)  评论(0编辑  收藏  举报