种下一棵树:有旋Treap

第一个平衡树板子,有旋Treap。用随机函数规定一个堆,维护点权的同时维护堆的性质,可以有效地避免退化成链。按我的理解,建立一棵二叉排序树,树的形态会和给出节点的顺序有关。按照出题人很机智定理,数据肯定不会太容易操作,这时候就需要我们自行调整“数据顺序”,平衡树应运而生。

这个板子涵盖的操作有左旋、右旋(维护堆性质)、添加节点、删除节点(建树相关)、查找第x个元素、查找元素排名、查找前驱、查找后继这8种操作。改变树本身的操作都是取地址传参的,询问操作则都是简单传参。原理不是太难理解,应用则看以后刷题了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
using namespace std;
const int sj=100010;
int n,opt,g,jg,cs,size;
struct tree
{
    int l,r,v,w,rnd,size;
}t[sj];
void update(int x)
{
    t[x].size=t[t[x].l].size+t[t[x].r].size+t[x].w;
}
void lturn(int &x)
{
     int tt=t[x].r;
     t[x].r=t[tt].l;
     t[tt].l=x;
     t[tt].size=t[x].size;
     update(x);
     x=tt;
}
void rturn(int &x)
{
     int tt=t[x].l;
     t[x].l=t[tt].r;
     t[tt].r=x;
     t[tt].size=t[x].size;
     update(x);
     x=tt;
}
void cr(int &x,int y)
{
     if(x==0)
     {
        size++;
        x=size;
        t[x].size=t[x].w=1;
        t[x].v=y;
        t[x].rnd=rand();
        return;
     }
     t[x].size++;
     if(t[x].v==y) t[x].w++;
     else if(y>t[x].v)
     {
         cr(t[x].r,y);
         if(t[t[x].r].rnd<t[x].rnd)  lturn(x);
     }
     else
     {
         cr(t[x].l,y);
         if(t[t[x].l].rnd<t[x].rnd)  rturn(x);
     }
}
void sc(int &k,int x)
{
     if(k==0)  return;
     if(t[k].v==x)
     {
        if(t[k].w>1)
        {
           t[k].w--;
           t[k].size--;
           return;
        }
        if(t[k].l*t[k].r==0) k=t[k].l+t[k].r;
        else if(t[t[k].l].rnd<t[t[k].r].rnd)
           rturn(k),sc(k,x);
        else
           lturn(k),sc(k,x);
     }
     else if(x>t[k].v)
          t[k].size--,sc(t[k].r,x);
     else
         t[k].size--,sc(t[k].l,x);
}
int query_rank(int k,int x)
{
    if(k==0)  return 0;
    if(t[k].v==x)  return t[t[k].l].size+1;
    else if(x>t[k].v)
      return t[t[k].l].size+t[k].w+query_rank(t[k].r,x);
    else return query_rank(t[k].l,x);
}
int query_num(int k,int x)
{
    if(k==0) return 0;
    if(x<=t[t[k].l].size)
      return query_num(t[k].l,x);
    else if(x>t[t[k].l].size+t[k].w)
      return query_num(t[k].r,x-t[t[k].l].size-t[k].w);
    else return t[k].v;
}
void query_pre(int k,int x)
{
     if(k==0) return;
     if(t[k].v<x)
        jg=k,query_pre(t[k].r,x);
     else query_pre(t[k].l,x);
}
void query_sub(int k,int x)
{
     if(k==0)  return;
     if(t[k].v>x)
        jg=k,query_sub(t[k].l,x);
     else query_sub(t[k].r,x);
}
int main()
{
    //freopen("t.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
       scanf("%d%d",&opt,&cs);
       if(opt==1)
         cr(g,cs);
       if(opt==2)
         sc(g,cs);
       if(opt==3)
         printf("%d\n",query_rank(g,cs));
       if(opt==4)
         printf("%d\n",query_num(g,cs));
       if(opt==5)
       {
          jg=0;
          query_pre(g,cs);
          printf("%d\n",t[jg].v);
       }
       if(opt==6)
       {
          jg=0;
          query_sub(g,cs);
          printf("%d\n",t[jg].v);
       }
    }
    //while(1);
    return 0;
}
时间: 2024-10-23 04:53:15

种下一棵树:有旋Treap的相关文章

1024某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。 由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点

#include<stdio.h> int main() { int L, M, i, j, n; int a[10001], b[10001]; scanf("%d %d",&L, &M); //输入L和M n = M*2;//循环输入b数组0~n的数据 for(i=0; i<n; i+=2)   { scanf("%d %d", &b[i], &b[i+1]); }   for(i=0; i<=L; i++

[您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)

今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我们还是用rand()来实现平衡 而无旋treap与treap不同的地方,也是其核心,就是它不旋转用两个新的核心函数:merge函数(合并两棵子树)和split函数(分裂出某棵树的前k个节点,并且作为一棵树返回) 首先看merge函数,它是一个递归实现的过程,先看代码: 1 Treap *merge(

种一棵树最好的时间是十年前,其次是现在

这不是一篇鸡汤,只是一个回头的娃的故事罢了. 1.前言 2016年堪称一个人生转折点,经历了太多的事情,这篇文章就当作一年的总结吧,虽然又拖延了两月才写完,不过有了更多的反思,反而更能看清自己,这个借口还可以.非得来一句鸡汤,那就是种一棵树最好的时间是十年前,其次是现在. 2.缘起 在各种公司里员工都会有级别之分,比如开发,高级开发和资深开发这样的Title划分,总会有一个能力层次的标识.我刚进公司时Title自然是开发,因此也会想知道如何才算高级或者资深的开发,努力的方向在哪里.在这之前,我一

四棵树怎么种才能使任意两颗之间距离相等

1.从宏观来看,把这四颗树看成四个质点 第一种办法是把它们种在一起,这样两两之间距离都为0: 第二种办法是使它们形成一个三棱锥,比如可以在山顶种一棵树,在山脚分三个方位种三棵树:或者在坑底种一棵树,坑外面分别种三棵树. 2.从微观来看,这四颗树都是有形状的,那么重点就在于他们之间的“距离”怎么定义. 第一.如果这四颗树不是笔直(树干长歪了,考虑树冠.枝干和叶子)的话,那么需要根据它们具体的形状来给出具体的解决方案,有兴趣的同学可以由此开发一个仿生数学模型: 第二.如果这四颗树都是笔直的,那么它们

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

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

非旋 treap 结构体数组版(无指针)详解,有图有真相

非旋  $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 所谓随机数维护平衡就是给每个节点一个随机值 key (下文中没有加随机的就代表是真实权值), 然后整棵树中 key 值要满足小(大)根堆的性质(也就是heap), 同时也要满足平衡树(tree)的性质(也就是每个节点左子树内节点真实

非旋Treap总结 : 快过Splay 好用过传统Treap

非旋$Treap$ 其高级名字叫$Fhq\ Treap$,既然叫$Treap$,它一定满足了$Treap$的性质(虽然可能来看这篇的人一定知道$Treap$,但我还是多说几句:$Fhp\ Treap$就是继承了$Treap$的随机系统,在二叉搜索的基础上,每个点加一个随机化处理,这些随机值满足堆的性质……通俗一点讲,就是$Fhp\ Treap$它每个点有至少两个值,一个是val,即存的数值,这些数值满足二叉搜索树,也就是父亲比左孩子小/大,则右孩子比父亲小/大:还有一个是key,是个随机值,这些

2827: 千山鸟飞绝 非旋treap

国际惯例的题面:看起来很不可做的样子,我们先来整理一下题意吧.就是,维护每个点曾经拥有过的最大的两个属性值,支持把点的位置移动.我们用map对每个位置进行离散化,对每个位置建立一个平衡树.为了方便分离,这个平衡树的关键字是节点编号.然后把每个点当做一个节点,放进其所在位置的平衡树里.剩下要做的就是平衡树分离出一个点,合并一个点,还有打标记了.对于士气值的标记,我们维护平衡树中的max,每次合并的时候,用这个新点的威武值去给整棵树打标记,再用树中的max给这个新点打标记.团结值的标记,合并后一起打

从前有棵树,叫高数,树上挂了很多人……

从前有棵树,叫高数,树上挂了很多人 很久很久以前,在拉格朗日照耀下,有几座城:分别是常微分方城和偏微分方城这两座兄弟城,还有数理方程.随机过城.从这几座城里流出了几条溪,比较著名的有:柯溪.数学分溪.泛函分溪.回归分溪.时间序列分溪等.其中某几条溪和支流汇聚在一起,形成了解析几河.微分几河.黎曼几河三条大河. 河边有座古老的海森堡,里面生活着亥霍母子,穿着德布罗衣.卢瑟服.门捷列服,这样就不会被开尔蚊骚扰,被河里的薛定鳄咬伤.城堡门口两边摆放着牛墩和道尔墩,出去便是鲍林.鲍林里面的树非常多:有高