【洛谷P3369】普通平衡树(splay)

emmmmm直接丢代码了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<stack>
#define ri register int
using namespace std;
const int mx = 100000 + 5;
struct in
{
    int sz,cnt,v;
    in *ch[2],*f;
    void su()//统计大小
    {
        sz = ch[0] -> sz + ch[1] -> sz + cnt;
    }
    int dir()//判断自己为左/右蛾子
    {
        return f -> ch[1] == this;
    }
    int cmp(int x)//判断该向哪里走
    {
        if(v == x)
            return -1;
        return x > v;
    }
    void setc(in *p,int d)//把这棵子树接到另一个点上
    {
        (ch[d] = p) -> f = this;
    }
}T[mx],*root,*null,*Null;
int tot;
in* newnode(in *p,int x)//建立新节点
{
    in *qwq = &T[++ tot];
    qwq -> v = x,qwq -> f = p,qwq -> cnt = qwq -> sz = 1;
    qwq -> ch[0] = qwq -> ch[1] = Null;
    return qwq;
}
void rotate(in *p)//旋转
{
    in *fa = p -> f;
    int d = p -> dir();//判断旋转方向:d^1
    fa -> f -> setc(p,fa -> dir());//先把p接到它父亲的位置上
    fa -> setc(p -> ch[d ^ 1],d),fa -> su();//然后把这个多出来的蛾子接到原来p在fa的位置上
    p -> setc(fa,d ^ 1),p -> su();//最后把fa接在d^1这边
    if(root == fa)//判断是不是更新根节点
        root = p;
}
void splay(in *p,in *rt)
{
    if(rt == p)
        return ;
    while(p -> f != rt)
    {
        if(p -> f -> f == rt)
        {
            rotate(p);break;
        }
        else
        {
            if(p -> dir() == p -> f -> dir())//如果同方向,先旋转父亲,再旋转本身
                rotate(p -> f),rotate(p);
            else//否则一直旋转本身
                rotate(p),rotate(p);
        }
    }
    p -> su();
    if(rt == null)//如果发现这个点被要求旋转到树根,则更新树根
        root = p;
}
void insert(in *p,int x)//插入
{
    if(root == Null)//如果一个点都没有
    {
        root = newnode(null,x);return;//null是树根,Null是空的意思
    }
    while(p -> ch[p -> v < x] != Null)//不停的走,x偏小向左,偏大向右,与该点相等停下
    {
        if(p -> v == x)
            break;
        p = p -> ch[p -> v < x];
    }
    if(p -> v == x)//判断下是不是与该点相等
    {
        p -> sz ++,p -> cnt ++,splay(p,null);return;
    }
    p -> ch[p -> v < x] = newnode(p,x);//建新点
    splay(p -> ch[p -> v < x],null);//只要是插入删除就splay一下维持平衡
}
in* find(int x)//查找一个点并返回它的位置
{
    in *rt = root;
    while(rt -> ch[x > (rt -> v)] != Null && x != (rt -> v))
        rt = rt -> ch[x > (rt -> v)];
    return rt;
}
in* next(int x,bool flag)//查找前驱后继
{
    in *rt = root,*qwq;
    int re;
    if(!flag)//前驱
    {
        re = 0;
        while(rt -> ch[0]||rt -> ch[1])
        {
            if(rt -> v >= x)
            {
                if(rt -> ch[0] != Null)//太大向左走
                    rt  = rt -> ch[0];
                else
                    break;
            }
            else
            {
                if(rt -> v > re)//选取尽可能和x接近的数
                    re = rt -> v,qwq = rt;
                if(rt -> ch[1] != Null)//太小向右走
                    rt = rt -> ch[1];
                else
                    break;
            }
        }
        if(rt -> v < x && rt -> v > re)//与最后到达的点进行比较
            re = rt -> v,qwq = rt;
    }
    else//后继,与前驱原理类似
    {
        re = 1000000007;
        while(rt -> ch[0]||rt -> ch[1])
        {
            if(rt -> v <= x)
            {
                if(rt -> ch[1] != Null)
                    rt  = rt -> ch[1];
                else
                    break;
            }
            else
            {
                if(rt -> v < re)
                    re = rt -> v,qwq = rt;
                if(rt -> ch[0] != Null)
                    rt = rt -> ch[0];
                else
                    break;
            }
        }
        if(rt -> v > x && rt -> v < re)
            re = rt -> v,qwq = rt;
    }
    return qwq;
}
void erase(int x)//删除
{
    in *rt = find(x);//先找到这个点
    if(rt -> cnt > 1)//如果这个点上的数出现了很多次,就cnt--
    {
        rt -> sz --,rt -> cnt --,splay(rt,null);return;
    }//否则把这个点彻底删除掉
    bool k = rt -> f -> ch[1] == rt;//判断该点是右蛾子还是左蛾子
    if(rt -> ch[0] == Null)
        if(rt -> ch[1] ==Null)//啥蛾子没有就直接删掉这个点
            rt -> f -> ch[k] = Null;
        else//如果只有一个蛾子就让它代替被删除点的位置
            rt -> f -> ch[k] = rt -> ch[1],rt -> ch[1] -> f = rt -> f;
    else
        if(rt -> ch[1] == Null)
            rt -> f -> ch[k] = rt -> ch[0],rt -> ch[0] -> f = rt -> f;
        else//否则如果左右蛾子都有的话 ,就让左蛾子顶替位置,然后把右蛾子放到左蛾子子树的最靠上的右空位(因为右蛾子的任何一个点都比做蛾子大,肯定在这棵子树的最右边)
        {
            in *ls = rt -> ch[0];
            rt -> f -> ch[k] = rt -> ch[0];
            rt -> ch[0] -> f = rt -> f;
            while(ls -> ch[1] != Null)
                ls = ls -> ch[1];
            ls -> ch[1] = rt -> ch[1];
            rt -> ch[1] -> f =ls;
            ls -> su(),splay(ls,null);
        }
    rt -> f -> su();
    splay(rt -> f,null);
}
int ask(int x)//查找一个数的排名,利用二叉搜索树本身左小右大的性质
{
    if(root == Null)
        return 0;
    in *rt = root;
    int re = 0;
    while(rt -> v != x)//直到找到这个数为止
    {
        bool fl = x > rt -> v;
        if(fl == 1)
            re += rt -> ch[0] -> sz + rt -> cnt;
        rt = rt -> ch[fl];
    }
    re += rt -> ch[0] -> sz + 1;//最后记录下左子树的大小然后再加上它本身
    splay(rt,null);
    return re;
}
int ask1(int x)//查找排x位的数
{
    in *rt = root;
    int num = rt -> ch[0] -> sz;
    while(!(x > num && x <= num + rt -> cnt))//括号里面表达式的意思为恰好找到排名为k的时候
    {
        if(x > num)//如果大于左子树,向右走,减去左侧排名
            x -= num + rt -> cnt,rt = rt -> ch[1];
        else//否则向左走
            rt = rt -> ch[0];
        num = rt -> ch[0] -> sz;
    }
    splay(rt,null);
    return rt -> v;
}
int n,opt,xx;
int main()
{
    Null = root = &T[++ tot],null = &T[++ tot],null -> ch[0] = null -> ch[1] = Null;//Null是为了初始赋值,null是根节点
    scanf("%d",&n);
    while(n --)
    {
        scanf("%d%d",&opt,&xx);
        if(opt == 1)
            insert(root,xx);//插入
        if(opt == 2)
            erase(xx);//删除
        if(opt == 3)
            printf("%d\n",ask(xx));//查询x的排名
        if(opt == 4)
            printf("%d\n",ask1(xx));//查询排名为x的数
        if(opt == 5)
            printf("%d\n",next(xx,0) -> v);//前驱
        if(opt == 6)
            printf("%d\n",next(xx,1) -> v);//后继
    }
}

原文地址:https://www.cnblogs.com/Loi-dfkdsmbd/p/8449498.html

时间: 2024-10-19 02:24:03

【洛谷P3369】普通平衡树(splay)的相关文章

AC日记——【模板】普通平衡树(Treap/SBT) 洛谷 P3369

[模板]普通平衡树(Treap/SBT) 思路: 劳资敲了一个多星期: 劳资终于a了: 劳资一直不a是因为一个小错误: 劳资最后看的模板: 劳资现在很愤怒: 劳资不想谈思路!!! 来,上代码: #include <cstdio> using namespace std; #define maxn 1000005 struct SplayTreeNodeType { int w,key,opi,size,ch[2]; }; struct SplayTreeNodeType tree[maxn];

洛谷 P3369 BZOJ 3224 【模板】普通平衡树(Treap/SBT)

题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删除一个) 查询x数的排名(若有多个相同的数,因输出最小的排名) 查询排名为x的数 求x的前驱(前驱定义为小于x,且最大的数) 求x的后继(后继定义为大于x,且最小的数) 输入输出格式 输入格式: 第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6) 输出格式: 对于操作3,4,5,6每行输出一个数,表示对应答案 输

[模板]洛谷T3369 普通平衡树 链表&amp;递归版、无父指针版Splay

结构体node定义:呐...因为不是Treap,所以就不必定义优先级yx了: 这次为了代码简短,总算是把判断子树方向函数(cmp)和节点信息维护函数(maintain)封在了结构体里. 旋转函数rotate:与Treap相比,没有任何变化,就是写得简短了一些. 插入函数insert:Treap时需要对违反堆性质的节点进行上浮的操作现在不需要了,只需同普通BST一样直接插入即可. 插完后,对刚插入的节点执行Splay操作. 删除函数del:针对待删除节点左右子树均非空的情况,Treap做法是将左右

[模板]洛谷T3391 文艺平衡树 链表&amp;递归版、无父指针版Splay

指针大法好 无父指针Splay大法好 大佬们的"改变旋转方向"萌新表示不懂,于是就自己乱搞出了下面的搞法... 代码如下,萌新写的丑,诸位大佬见谅QwQ~ 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<ctime> 5 #include<cstdlib> 6 #include<ctime> 7 8 #include<

洛谷 P3369 【模板】普通平衡树(Treap/SBT)

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

洛谷3391 文艺平衡树 平衡树模板95

上次被火星人prefix吊打,突然发现已经不会写splay了 于是来道模板题 区间反转? 打一些lazy标记,感觉和线段树没有差太多,而且交换左右儿子这操作真是妙 lazy标记 下传的时间要注意有些东西会变 1 #include <bits/stdc++.h> 2 #define N 500000 3 #define mid (l+r>>1) 4 using namespace std; 5 int n,m,x,y; 6 struct spla 7 { 8 int fa[N],c[

[P3369]普通平衡树(Splay版)

模板,不解释 #include<bits/stdc++.h> using namespace std; const int mxn=1e5+5; int fa[mxn],ch[mxn][2],sz[mxn],cnt[mxn],val[mxn],rt,tot; namespace Splay { void push_up(int x) { sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x]; }; void rotate(int x) { int y=fa[x],z=f

[模板]洛谷T3369 普通平衡树 链表&amp;普通Treap

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<ctime> 6 #include<cmath> 7 #include<string> 8 #include<stack> 9 #include<queue> 10 #include<vector> 11 #

在洛谷3369 Treap模板题 中发现的Splay详解

本题的Splay写法(无指针Splay超详细) 前言 首先来讲...终于调出来了55555...调了整整3天..... 看到大部分大佬都是用指针来实现的Splay.小的只是按照Splay的核心思想和原理来进行的.可能会有不妥之处,还请大佬们指出,谢谢! 那么这个题解存在的意义就是让不会敲Splay的人额...会敲Splay啦... 基本思想 数据结构 对于Splay,我定义了一个class类(当成struct就行啦...个人习惯不同啦),定义名称为“Splay”. 之后在类中,我定义了Splay