Treap non rotatif (treap FHQ)

Treap non rotatif (treap FHQ)

Je ne comprends pas le splay, alors j’ai appris le treap non rotatif
. L’efficacité du treap est en fait à peu près la même que l’écartement.

Opération de base

fhq treap et treap en ont aussi unValeur rnd assignée au hasard, Utilisé pour équilibrer (sans changer la nature, faire en sorte que la forme de l'arbre ait tendance àAléatoirePour assurer l'équilibre), mais fhq treapAucune rotation requisePour maintenir l'équilibre, car il y a deux opérations magiques fusionner (fusionner) et diviser (scinder)

1, divisé (divisé)

Petit à gauche, grand à droite
1. Division par poids
Effet: un sous-arbre enraciné à un certain nœudDivisé en deux nouveaux sous-arbres , De ces deux sous-arbresNoeud principalRespectivement, x, y.
Là où x sous-arbre enraciné au bon point, les valeurs sont toutes inférieures ou égales à v, y est le poids de tous les points de la racine du sous-arbre sont supérieurs à v.

Opération:
Pour le sous-arbre enraciné au nœud now:
1. Le poids de now est inférieur ou égal à v: Connectez - vous maintenant avec x pour diviser le sous-arbre de droite (le fils de gauche a accès à x dans cette opération)
2. Le poids de now Supérieur à v: connectez-vous maintenant avec y , divisez le sous-arbre gauche (le fils droit est connecté à y dans cette opération)
(l'opération de division équivaut à connecter chaque nœud avec les nouveaux fils gauche et droit) la
mise à jour est maintenant
recommandée pour combiner la compréhension du code

void split(int now,int k,int &x,int &y){
    
    //注意观察&的用途
	if(!now) x=y=0;//结束
	else{
    
    
		if(tr[now].v<=k)
		x=now,split(tr[now].son[1],k,tr[now].son[1],y);
		//x=now相当于tr[祖先].son[1]=now ,右子树中剩下的委派给右儿子。(对祖先也是如此)
		else
		y=now,split(tr[now].son[0],k,x,tr[now].son[0]);
		//y=now相当于tr[祖先].son[0]=now , 左子树中剩下的委派给左儿子。(对祖先也是如此)
		update(now);//更新 ( 因为这棵树已经“重构” )
	}
}

Divisé
Lorsque vous résolvez le problème d'intervalle, divisez-le par taille.
À ce stade, k est la taille. Si le sous-arbre gauche de l'arborescence maintenant a> = k nœuds, trouvez k à partir de son sous-arbre gauche.
Sinon, trouvez k à partir de son sous-arbre droit. tree [lson] .size-1 (le côté gauche apporte suffisamment de points) Le 1 soustrait est le nœud racine (un peu comme treap pour trouver le kème le plus grand)

void split(int now, int k, int &x, int &y) { // 前k个值组成x,k+1到最后一个值组成y
	if(!now) x = y = 0;
	else {
		if(tr[now].lz) down(now);
		if(k <= tr[tr[now].ls].siz) //画画图理解一下,k在now的左子树里就执行下面的语句
			y = now, split(tr[now].ls, k, x, tr[now].ls); //now及其右树已分到y上,现在分now的左子树,并且y的等待位置是y的左子节点
		else 
			x = now, split(tr[now].rs, k-tr[tr[now].ls].siz-1, tr[now].rs, y);
		update(now);
	}
}

2. Fusionner

RécursionLe processus consistant à s'assurer que la valeur val de tous les points de x (la valeur à stocker, pas une valeur aléatoire) doit être garantie avant l'exécutionSont inférieurs ày (appelé x <y), c'est la nature de l'arbre de recherche binaire, et comment s'assurer que cette propriété ne vous inquiétez pas, dans l' opération d' insertion , cette propriété est naturellement réalisée. Enfin, revenez au nœud racine actuel

int merge(int x,int y){
	if(!x||!y) return x+y;//即返回非0的那个
	if(tr[x].rnd<tr[y].rnd){//rnd是随机因子
		tr[x].son[1]=merge(tr[x].son[1],y); //x为根
		update(x);
		return x;
	}
	else{
		tr[y].son[0]=merge(x,tr[y].son[0]);//y为根
		update(y);
		return y;
	} 
}

Faites attention à la maintenance en temps réel de la racine pendant le fonctionnement

Opération de base

1. Créez un nouveau nœud
int new_code(int v)
{
	tot++;
	tr[tot].size=1;tr[tot].v=v;tr[tot].rnd=rand();
	return tot;
}
2. mettre à jour

Les nœuds vides ne peuvent pas être mis à jour, car siz sera 1

int new_node(int v)
{
	tot++;
	tr[tot].size=1;tr[tot].v=v;tr[tot].rnd=rand();
	return tot;
}
3. Insérez

Afin de garantir la nature de "x <y", nous avons dû diviser par val, puis lors de la fusion de x et de point new, bien que x ait la même valeur que new, le problème n'est pas grand.

split(root, val, x, y);
root = merge(merge(x, new_node(val)), y);

4. Supprimer

Notez que lors de la fusion, vous devez inverser l'ordre de division, toujours pour vous assurer que
l'opération sous "x <y" sépare avec succès un c, et que la valeur de la racine de c doit être val, c'est à cause de la division précédenteUNE, A n'a que deux types de points, le point dont la valeur est val et le point dont la valeur est <= val, etc est séparé de a à nouveau

split(root, val, a, b);
split(a, val-1, a, c);
c = merge(tree[c].lson, tree[c].rson);
root = merge(merge(a,c), b);//a,c最后拆,最先合

5. Interrogez le numéro de rang k
int kth(int now,int k){
	while(1){
		if(k<=tr[tr[now].son[0]].size)//就向左
		now=tr[now].son[0];//查前驱时若前驱为空会在这里死循环,所以有时要设哨兵节点
		else{
			if(k==tr[tr[now].son[0]].size+1) return now;
			else
			{
				k-=tr[tr[now].son[0]].size+1;
				now=tr[now].son[1];//向右
			}
		}
	}
}
6. Trouvez le rang de val

Divisez l'arbre entier en x et y en divisant val-1, et la réponse est x.size + 1, bien sûr, le principe est que cette valeur existe

7. Trouvez le précurseur de val

Divisez par val-1, trouvez le plus grand dans l'arbre de gauche, c'est-à-dire trouvez le rang dans l'arbre de gauche comme la taille de l'arbre de gauche

8. Trouvez le successeur de val

Divisé par val, le bon rang 1 de l'arbre est


P3369 [Template] Arbre équilibré ordinaire
.

#include<bits/stdc++.h>
using namespace std;
const int N = 100010;
struct trnode{
	int son[2],v,rnd,size;
}tr[N];
int tot = 0,root=0;

void update(int x){
	tr[x].size=tr[tr[x].son[0]].size+tr[tr[x].son[1]].size+1;
}
int new_code(int v)
{
	tot++;
	tr[tot].size=1;tr[tot].v=v;tr[tot].rnd=rand();
	return tot;
}
int merge(int x,int y){
	if(!x||!y) return x+y;
	if(tr[x].rnd<tr[y].rnd){
		tr[x].son[1]=merge(tr[x].son[1],y);
		update(x);
		return x;
	}
	else{
		tr[y].son[0]=merge(x,tr[y].son[0]);
		update(y);
		return y;
	}
}
void split(int now,int k,int &x,int &y){
	if(!now) x=y=0;
	else{
		if(tr[now].v<=k)
		x=now,split(tr[now].son[1],k,tr[now].son[1],y);
		else
		y=now,split(tr[now].son[0],k,x,tr[now].son[0]);
		update(now);
	}
}
int kth(int now,int k){
	while(1){
		if(k<=tr[tr[now].son[0]].size)
		now=tr[now].son[0];
		else{
			if(k==tr[tr[now].son[0]].size+1) return now;
			else
			{
				k-=tr[tr[now].son[0]].size+1;
				now=tr[now].son[1];
			}
		}
	}
}
int main(){
	srand(902);
	int T;int flag ,x,y,z,a,b;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&flag,&a);
		if(flag==1){
			split(root,a,x,y);
			root=merge(merge(x,new_code(a)),y);
		}
		else if(flag == 2){
			split(root,a,x,z);
			split(x,a-1,x,y);
			y=merge(tr[y].son[0],tr[y].son[1]);
			root=merge(merge(x,y),z);
		}
		else if(flag==3){
			split(root,a-1,x,y);
			printf("%d\n",tr[x].size+1);
			root=merge(x,y);
		}
		else if(flag==4)
		printf("%d\n",tr[kth(root,a)].v);
		else if(flag==5){
			split(root,a-1,x,y);
			printf("%d\n",tr[kth(x,tr[x].size)].v);
			root=merge(x,y);
		}
		else if(flag == 6){
			split(root,a,x,y);
			printf("%d\n",tr[kth(y,1)].v);
			root=merge(x,y);
		}
	}
	return 0;
}

Questions pratiques:

P3391 [Template] Arbre d'équilibre littéraire -> Opération Flip
P1503 Le diable entre dans le village -> Nœud sentinelle

Veuillez aimer QWQ

Je suppose que tu aimes

Origine blog.csdn.net/Josh_Shu_/article/details/109908880
conseillé
Classement