替罪羊树

这是一篇低质量博客

但是笔者自己看效果可能很好(写给自己看的博客)

一、前言

失踪人口终于回归啦~

替罪羊树太简单了! 以致于板题调了两天呢!

二、讲解

我们已经学过很多平衡树了,对于平衡树满足的特点,我就不多赘述了

我们直接步入正题,讲解替罪羊树维持平衡的方法

首先我们要知道怎样才算不平衡,这样我们才好修复

直接yy:如果一个点的某一个儿子太重了,那么这棵子树就不平衡了

上面的yy过于感性,我们来定量分析一下:

我们引入替罪羊树专有的变量:(alpha)平衡因子

如果重儿子的重量 (>) 整棵子树的重量 ( imes alpha),那么我们认为它不平衡

我们一般把(alpha)定为(0.5)~(1)的数(()个人喜欢(0.7))

别问,问就是7777777777777777

那么如果树不平衡了怎么办呢?

暴力重建!

当然不是整棵树,而是不平衡的最上面的那棵子树

比如子树(A,B)都不平衡,而子树(B)属于子树(A),那么我们要重建的是子树(A)而非子树(B),因为重建了子树(B),子树(A)依然不平衡

由于只有插入和删除操作会影响,所以在每次插入或删除操作后,我们检查一下是否存在不平衡的子树,有则改之,无则加勉

检查的细节是插入和删除只有一个元素,所以只需要检查 以这个元素到根的路径上的点的子树是否平衡就行了

重建没什么好说的吧,把要重建的点拖出来重新排一下就好了

0、结构体定义

int son[2],val,siz,cnt,fa;
//左右儿子(0,1) 元素值 子树大小 元素个数 当前节点的父亲
bool exist;
//是否存在(其实直接看cnt是否为0就好了,我码麻烦了)

1、插入

其实插入没什么好讲的

如果存在插入的元素,(cnt++)

不存在,找到应该放的位置,放进去就好了

插入之后记得更新这条链上的子树大小

2、删除

删除有两种方法

一是打上标记(貌似不需要打标记,只需要检查次数就好了)

二是把它的儿子提上来

我是前者

找到该元素,(cnt--)

如果(cnt==0),打上消失的标记(也可以不打,我们可以根据(cnt)是否为(0)来判断)

记得更新这条链上的子树大小

3、其他操作

和其它的平衡树类似,不再展开

关于代码:笔者首次采用了内存池的打法,个人感觉这种写法很好,值得一学(你不学我也不拦着你)

三、练习

板题(洛谷)

带插入区间K小值(洛谷)

四、代码

板题代码

//12252024832524
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; 

typedef long long LL;
const int MAXN = 100005;
const int INF = 0x3f3f3f3f; 
int n;

int Read()
{
	int x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
void Put1(int x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
void Put(int x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x);
	if(c >= 0) putchar(c);
}
template <typename T>T Max(T x,T y){return x > y ? x : y;}
template <typename T>T Min(T x,T y){return x < y ? x : y;}
template <typename T>T Abs(T x){return x < 0 ? -x : x;}

struct ScapegoatTree
{
	int rt,tot,ncc[MAXN],ncctot;//根 点 内存池 内存池的点 
	int retot,re[MAXN][2];//0 val 1 cnt
	double alpha = 0.7; //平衡因子 
	
	struct node
	{
		int son[2],val,siz,cnt,fa;
		bool exist;
	}t[MAXN];
	
	int newnode()
	{
		if(ncctot > 0) return ncc[ncctot--];
		return ++tot;
	}
	int find(int x,int val)
	{
		if(t[x].val > val && t[x].son[0]) return find(t[x].son[0],val);
		if(t[x].val < val && t[x].son[1]) return find(t[x].son[1],val);
		return x;
	}
	void build(int x,int val,int fa)
	{
		t[x].son[0] = t[x].son[1] = 0;
		t[x].fa = fa;
		t[x].exist = 1;
		t[x].val = val;
		t[x].siz = t[x].cnt = 1;
	}
	void update(int x)
	{
		if(!x) return ;
		t[x].siz = t[t[x].son[0]].siz + t[t[x].son[1]].siz + t[x].cnt;
		update(t[x].fa);
	}
	void redfs(int x)
	{
		if(!x) return;
		redfs(t[x].son[0]);
		if(t[x].exist) re[++retot][0] = t[x].val,re[retot][1] = t[x].cnt;
		ncc[++ncctot] = x;
		redfs(t[x].son[1]);
	}
	int renow(int l,int r,int fa)
	{
		if(l > r) return 0;
		int ID = newnode(),mid = (l+r) >> 1;
		t[ID].val = re[mid][0];
		t[ID].cnt = re[mid][1];
		t[ID].fa = fa;
		t[ID].son[0] = renow(l,mid-1,ID);
		t[ID].son[1] = renow(mid+1,r,ID);
		t[ID].siz = t[t[ID].son[0]].siz + t[t[ID].son[1]].siz + t[ID].cnt;
		t[ID].exist = 1;
		return ID; 
	}
	void rebuild(int x)
	{
		retot = 0;
		redfs(x);
		if(x == rt) rt = renow(1,retot,0);
		else
		{
			if(t[t[x].fa].son[0] == x) t[t[x].fa].son[0] = renow(1,retot,t[x].fa);
			else t[t[x].fa].son[1] = renow(1,retot,t[x].fa);
			update(t[x].fa);
		}
	}
	void find_rebuild(int x,int val)
	{
		if(!x) return;
		if(1.0 * Max(t[t[x].son[0]].siz,t[t[x].son[1]].siz) > t[x].siz * alpha) {rebuild(x);return;}
		if(t[x].val != val) find_rebuild(t[x].son[val > t[x].val],val);
	}
	void ins(int val)
	{
		if(!rt)
		{
			build(rt = newnode(),val,0);
			return;
		}
		int dz = find(rt,val);
		if(val == t[dz].val)
		{
			t[dz].cnt++;
			t[dz].exist = 1;
			update(dz);
		}
		else
		{
			if(val < t[dz].val) t[dz].son[0] = newnode(),build(t[dz].son[0],val,dz),update(t[dz].son[0]);
			else t[dz].son[1] = newnode(),build(t[dz].son[1],val,dz),update(t[dz].son[1]);
		}
		find_rebuild(rt,val);
	}
	void del(int val)
	{
		int dz = find(rt,val);
		if(t[dz].val == val) 
		{
			t[dz].cnt --;
			if(!t[dz].cnt) t[dz].exist = 0;
			update(dz);
			find_rebuild(rt,val);
		}
	}
	int rk(int val)
	{
		int now = rt,ret = 0;
		while(t[now].val != val && now)
		{
			if(t[now].val < val) ret += t[now].cnt + t[t[now].son[0]].siz,now = t[now].son[1];
			else now = t[now].son[0];
		}
		ret += t[t[now].son[0]].siz;
		return ret + 1;
	}
	int findrk(int x) 
	{
		int now = rt;
		while(1)
		{
			if(t[t[now].son[0]].siz >= x) now = t[now].son[0];
			else if(t[t[now].son[0]].siz + t[now].cnt < x) x -= t[t[now].son[0]].siz + t[now].cnt,now = t[now].son[1];
			else return t[now].val;
		}
	}
	int pre(int val)
	{
		return findrk(rk(val)-1);
	} 
	int suc(int val)
	{
		return findrk(rk(val+1));
	}
	void debug(int x)
	{
		if(!x) return;
		debug(t[x].son[0]);
		if(t[x].exist)
			Put(x,' '),Put(t[x].val,'
');
		debug(t[x].son[1]);
//		update(x);
	}
}s;

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	for(int T = Read(); T ;-- T)
	{
		int opt = Read(),x = Read();
		if(opt == 1) s.ins(x);//插入x
		else if(opt == 2) s.del(x);//删除x
		else if(opt == 3) Put(s.rk(x),'
');//查询x的rank
		else if(opt == 4) Put(s.findrk(x),'
');//查询rank为x的数 
		else if(opt == 5) Put(s.pre(x),'
');//查询x的前缀 
		else Put(s.suc(x),'
');//查询x的后缀 
	}
	return 0;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/13392267.html