平衡树 fhqTreap

//Treap fhq版(不旋转)
//所有操作依靠split()(分离)和merge()(合并)完成
//可支持区间操作和可持久化 比普通Treap更通用
//所有Treap中序遍历均为递增序列
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
using namespace std;
int n,cnt,size,root,x,y,z;
//x为小Treap的根节点 y为中Treap的根节点 z为大Treap的根节点 root为总Treap的根节点
int son[100001][2],siz[100001],val[100001],rd[100001];
//左右儿子,子树大小+自己大小,点值,随机权值
int new_node(int m)//建立新节点
{
    siz[++size]=1;
    val[size]=m;
    rd[size]=rand();
    return size;
}
void update(int now)
{
    siz[now]=siz[son[now][0]]+siz[son[now][1]]+1;
}
void split(int now,int k,int &a,int &b)
{
    if(!now)
    {
        a=b=0;
        return;
    }
    if(val[now]<=k)
        a=now,split(son[now][1],k,son[now][1],b);
        //将此节点及其左子树接到小Treap上,并遍历此节点的右节点
    else b=now,split(son[now][0],k,a,son[now][0]);
        //将此节点及其右子树接到大Treap上,并遍历此节点的左节点
    update(now);
}
int merge(int a,int b)
{
    if(!a||!b)
        return a+b;
    if(rd[a]<rd[b])
    {
        son[a][1]=merge(son[a][1],b);
        //将小Treap节点及其左子树接在总Treap上,并遍历此节点的右节点
        update(a);
        return a;
    }
    else
    {
        son[b][0]=merge(a,son[b][0]);
        //将大Treap节点及其右子树接在总Treap上,并遍历此节点的左节点
        update(b);
        return b;
    }
}
int k_th(int now,int k)
{
    while(1)
    {
        if(k<=siz[son[now][0]])
            now=son[now][0];
        else if(k==siz[son[now][0]]+1)
            return now;
        else
        {
            k-=siz[son[now][0]]+1;
            now=son[now][1];
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int u,k;
        scanf("%d%d",&u,&k);
        if(u==1)//插入
        {
            split(root,k,x,z);//先分成两个Treap 此时k(若有)在小Treap中
            root=merge(merge(x,new_node(k)),z);//重新合并
        }
        if(u==2)//删除
        {
            split(root,k,x,z);//先分成两个Treap 此时k在小Treap中
            split(x,k-1,x,y);//再把小Treap分成两个Treap 此时k在中Treap中 且为中Treap根节点
            y=merge(son[y][0],son[y][1]);//把中Treap根节点的两个子树合并 此时已删除k
            root=merge(merge(x,y),z);//重新合并
        }
        if(u==3)//查找权值为k的排名
        {
            split(root,k-1,x,z);//先分成两个Treap 此时小Treap中包含了所有小于k的数
            printf("%d\n",siz[x]+1);//k的排名即为小于k的权值数目+1
            root=merge(x,z);//重新合并
        }
        if(u==4)//查找排名为k的权值
            printf("%d\n",val[k_th(root,k)]);//直接输出
        if(u==5)//求前驱
        {
            split(root,k-1,x,z);//先分成两个Treap 此时k是大Treap中最小的数
            printf("%d\n",val[k_th(x,siz[x])]);//前驱即为小Treap中最大的数 即最右节点
            root=merge(x,z);//重新合并
        }
        if(u==6)//求后继
        {
            split(root,k,x,z);//先分成两个Treap 此时k是小Treap中最大的数
            printf("%d\n",val[k_th(z,1)]);//后继即为大Treap中最小的数 即最左节点
            root=merge(x,z);//重新合并
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/water-radish/p/9280878.html

时间: 2024-10-19 00:24:35

平衡树 fhqTreap的相关文章

P3369 【模板】普通平衡树FHQtreap

P3369 [模板]普通平衡树(Treap/SBT) 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删除一个) 查询x数的排名(排名定义为比当前数小的数的个数+1.若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数) 求x的后继(后继定义为大于x,且最小的数) 输入输出格式 输入格式: 第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(

平衡树 fhqTreap 区间操作

//Treap fhq版(不旋转) //此模板为平衡树维护区间操作的模板 //注:在区间操作中split()标准变为子树大小 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f #de

Luogu P3391 【模板】文艺平衡树(FHQ-Treap)

题意 给出一个长为$n$序列$[1,2,...,n]$,$m$次操作,每次指定一段区间$[l,r]$,将这段区间翻转,求最终序列 题解 虽然标题是$Splay$,但是我要用$FHQ\ Treap$,考虑先将$[l,r]$这段区间$split$出来($k$即为这段区间) void split(int o, int k, int &l, int &r) { if(!o) { l = r = 0; return ; } if(siz[lc[o]] < k) l = o, split(rc[

[luogu3391] 【模板】文艺平衡树(fhq-treap反转区间)

解题关键:无旋treap模板. #include<iostream> #include<cstdio> #include<cstring> #include<ctime> #include<cstdlib> #include<cmath> #include<algorithm> #define maxn 500001 using namespace std; typedef long long ll; int size[m

fhq-Treap 文艺平衡树代码记录

#include<iostream> #include<algorithm> #include<vector> #include<cstdio> #include<string> using namespace std; const int N=1e4+10; int a[N]; int root; int idx; int x,y,z; struct node{ int l,r; int size; int val; int key; int

fhqtreap的学习笔记

一段时间之前学的fhqtreap--今天闲的没事稍微总结一下吧--学了fhq之后就再也不想写splay了. fhqtreap的优点:速度快,代码简单,可以进行区间操作,而且也可以进行可持久化. //学习这个首先得懂平衡树和可并堆. 首先要明确一点:fhqtreap本质上还是一个treap,也就是说它也是靠堆权值来维护树的平衡特性的.但是fhqtreap没有旋转,只有两个最简单的操作split(分裂)和merge(合并),通过这两个操作来维护平衡树的性质.我们用w表示平衡树里存的权值,f表示堆权值

【专题】平衡树

[旋转] 平衡树中的旋转是指在不改变中序遍历的前提下改变树的形态的方式.(中序遍历=排名顺序) 右旋将当前点的左节点旋上来,左旋反之.(图侵删) void rturn(int &k){ int o=t[k].l; t[k].l=t[o].r; t[o].r=k; up(k);up(o); k=o; } 原根k,新根o. 1.把k的左节点o解放出来并更新为o的右节点. 2.解放出来的o成为新根,其右孩子赋为k. [Treap]树堆 功能:维护支持单点插入和单点删除的排名树. 特点:给每个节点随机堆

【算法】fhqtreap初探

NOIP回来就一直想着学平衡树...平衡树写久了调不出来真的会头脑发热.jpg 大概只写了几道题... fhqtreap是不需要旋(xun)转(jun)的平衡树,仅使用分裂合并,一样可以保持平衡树的性质,并且可以非常简单地处理区间问题. fhqtreap的核心有两端代码,split(分裂)和merge(合并) split(x, l, r, k),表示把原x的子树以第k大数为界限,权值<=第k大数的数分在左子树,根为l,其他的分在右子树,根为r void split(int x, int &l

【算法学习】Fhq-Treap(无旋Treap)

Treap--大名鼎鼎的随机二叉查找树,以优异的性能和简单的实现在OIer们中广泛流传. 这篇blog介绍一种不需要旋转操作来维护的Treap,即无旋Treap,也称Fhq-Treap. 它的巧妙之处在于只需要分离和合并两种基本操作,就能实现任意的平衡树常用修改操作. 而不需要旋转的特性也使编写代码时不需要考虑多种情况和复杂的父亲儿子关系的更新,同时降低了时间复杂度. 此外,它还可以方便地支持可持久化,实在是功能强大. 接下来系统性地介绍一下无旋Treap的原理和实现,最后讲解一下应用和例题.