splay总结

以此文纪念人生首次竞赛大选

这里主要讲一讲splay的区间操作,我讲的是指针实现,程序的效率可能比较低,更偏重代码的可读可写性,语言风格不是很优美有效,不喜勿喷

零、初始化结构体

1)这里主要是初始化结构体,记得先生成一个null节点指针,代表一切未使用的、未开发的节点,root也先赋值为null

2)结构体成员:有几个指针,如父亲(没有也可以,只不过令splay失去一些平衡树的功能),左右儿子,这里用数组记录,下标为0,1,方便我们操作

有几个需要维护的域,如size,子树和根的大小(包括重复节点),mul,重复节点的计数器,rev,区间翻转标记,一层一层地传,addnum,区间加的标记,cov,区间覆盖标记

3)结构体函数:可以自己写一个构造函数,可以声明后在结构体外写,就可以用null指针了,但最好是在结构体外写一个newnode,这样更清晰且更灵活

4)下传函数(relax/pushdown):

DEBUG:这里注意每次下传rev时,要马上实现交换左右儿子

DEBUG:这里注意所有的,如max域和sum域也要进行区间加操作

主要实现了区间加、区间覆盖、区间翻转的标记的下传

DEBUG:记得从上到下pushdown

5)更新函数(pushup/update):

根据需要看max域从儿子传上来挑最大,size域加儿子的size和自己的mul,不考虑重复时就用1代替

DEBUG:记得从下到上pushup

一、插入/构造

DEBUG:一下所有的涉及改动的操作记得用址传,不然会出大事哦

1)区间插入,先说进阶的,我们直接对构造一棵平衡树后,将所需区间伸展至根节点右儿子的左儿子处,将构造号的树塞到那里

DEBUG:这里注意塞完树以后记得更新根节点右儿子和根节点的维护域

2)单点插入,和区间一样,伸展所需位置,插入即可

DEBUG:一样滴,这里注意塞完树以后记得更新根节点右儿子和根节点的维护域

(似乎最后将插入的区间做一次splay可以提高效率)

二、删除

主要讲讲区间吧,先把区间伸展到指定位置,我是用递归实现整颗子树的删除的,先递归非空左右儿子,后用系统关键字delete删除当前指针

DEBUG:这里一样要更新有关节点,即pushup

三、旋转

这就不需要多说,平衡树的基础知识

我的函数是指定当前节点,将其对父亲旋转

DEBUG:注意下操作顺序

先提取出父亲的指针,再把当前节点接到父亲的位置

此后把当前和节点异侧的儿子接到父亲连当前节点的位置

之后把父亲接到这个儿子的位置

记得更新一下

四、splay函数/伸展操作

简化一下,主要分了三种情况

第一,当前节点的爷爷是目标节点,直接旋转当前节点

否则

第二,当前节点和父亲不在同侧,旋转当前节点两次

第三,当前节点和父亲在同侧,就先旋转父亲,再旋转当前节点

DEBUG:在函数结尾记得对当前节点进行更新操作,因为旋转函数的更新可能不完全

五、求第k个

这个没什么好说的,我是用循环实现的,非递归

可能此后还有一些进阶操作,这里就不提及了,那不是总结可以穷尽的

最后放个板

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define mid ((x>>1)+(y>>1)+(x&y&1))
using namespace std;
const int N = 1e4+10;
const int inf = ~0U>>1;
struct nt{
    nt*ch[2],*p;

    int k,size;

    int add;bool rev;

    bool d(){
        return this==p->ch[1];
    }

    void setc(nt*c,int d){
        ch[d]=c;
        c->p=this;
    }

    void addIt(int ad){
        k+=ad;
        add+=ad;
    }

    void revIt(){
        rev^=1;
        swap(ch[0],ch[1]);
    }

    void pu(){
        size=1+ch[0]->size+ch[1]->size;
    }

    void relax(){
        if(rev)
            ch[0]->revIt(),
            ch[1]->revIt(),
            rev=0;
        if(add)
            ch[0]->addIt(add),
            ch[1]->addIt(add),
            add=0;
    }
};

nt*null=new nt();
nt*root=null;
int a[N];

nt*make(nt*p,int k){
    nt*now=new nt();
    now->size=1;
    now->k=k;
    now->p=p;
    now->ch[0]=now->ch[1]=null;
    return now;
}

void rot(nt*&o){
    nt*p=o->p;
    p->relax();
    o->relax();
    bool d=o->d();
    p->p->setc(o,p->d());
    p->setc(o->ch[!d],d);
    o->setc(p,!d);
    p->pu();o->pu();
    if(p==root)root=o;
}

void splay(nt*o,nt*p){
    while(o->p!=p)
        if(o->p->p==p)
            rot(o);
        else
            o->d()^o->p->d()?(rot(o),rot(o)):(rot(o->p),rot(o));
    o->pu();
}

nt*build(int x,int y){
    if(x>y)return null;
    nt*o=make(o,a[mid]);
    o->setc(build(x,mid-1),0);
    o->setc(build(mid+1,y),1);
    o->pu();
    return o;
}

void del(nt*&o){
    if(o->ch[0]!=null)del(o->ch[0]);
    if(o->ch[1]!=null)del(o->ch[1]);
    delete o;
}

nt*kth(int k){
    for(nt*o;;){
        o->relax();
        if(k<=o->ch[0]->size)
            o=o->ch[0];
        else{
            k-=o->ch[0]->size+1;
            if(!k)return o;
            o=o->ch[1];
        }
    }
}

int main(){
    int n;
    /*
    *a=read(1~n)
    */
    root=build(0,n+1);
    root->p=null;
    /*
    insert l,r?
    *a=read(1~r)
    splay(kth(l+1),null)
    splay(kth(l+2),root)
    root->ch[1]->setc(build(1,r),0)
    root->ch[1]->pu()
    root->pu()
    */
    return 0;
}

完结,撒花~~~

时间: 2024-11-07 01:11:03

splay总结的相关文章

BZOJ 1861 [Zjoi2006]Book 书架 ——Splay

[题目分析] 模板题目. 首尾两个虚拟结点,十分方便操作. [代码] #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue> #include <string> #include <iostream> #include

及其简短的Splay代码

#include <stdio.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <math.h> #include <iostream> #define inf 1000000000 using namespace std; #define getch() getchar() inline int F() {register int aa ,

BZOJ_1014_[JSOI2008]_火星人prefix_(Splay+LCP_Hash+二分)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1014 给出一个字符串,有修改,插入,以及询问LCP(i,j)的操作. 分析 LCP在白书上面有介绍,\(LCP(i,j)\)表示以第\(i\)位和以第\(j\)位开头的后缀的最长公共前缀. 先考虑没有插入和修改操作的问题.我们可以用基于Hash的LCP算法. 我们给每一个后缀一个Hash值.其中以第\(i\)为开头的后缀的Hash值为\(H[i]=H[i+1]x+s[i]\). 其中\(x\

BZOJ 1251 序列终结者(Splay)

题目大意 网上有许多题,就是给定一个序列,要你支持几种操作:A.B.C.D.一看另一道题,又是一个序列要支持几种操作:D.C.B.A.尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思.这道题目 就叫序列终结者吧.[问题描述] 给定一个长度为N的序列,每个序列的元素是一个整数(废话).要支持以下三种操作: 1. 将 [L, R] 这个区间内的所有数加上 V. 2. 将 [

CSU 1555 Inversion Sequence 给出逆序数求排列 splay

题目链接:点击打开链接 题意: 给出逆序数的值,求原序列(一个1-N的排列) 1, 2, 0, 1, 0 表示1的逆序数是1,2的逆序数是2,3的逆序数是0··· 思路: 从最后一个数开始插,每次插到当前序列的第a[i]个数.. splay模拟 == 这个方法比较直(wu)观(nao),别的方法并没有想出来.. #include <cstdio> #include <iostream> #include <cstring> #include <queue>

【UOJ】【UR #2】猪猪侠再战括号序列(splay/贪心)

http://uoj.ac/problem/31 纪念伟大的没有调出来的splay... 竟然那个find那里写错了!!!!!!!!!!!!! 以后要记住:一定要好好想过! (正解的话我就不写了,太简单了.. #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <iostream> #include <algorithm> #

BZOJ 1507 NOI2003 Editor Splay

题目大意: 1.将光标移动到某一位置 2.在光标后插入一段字符串 3.删除光标后的一段字符 4.输出光标后的一段字符 5.光标-- 6.光标++ 和1269很像的一道题,不过弱多了 几个问题需要注意: 1.插入的字符串中间居然会有回车!!没办法了,只能逐个字符进行读入,一旦读到'\n'或者'\r'就重新读入 2.题目描述中说Delete和Get操作后面一定会有足够的字符 纯属放P 连样例都没有足够的字符用来删除 所以删除时要和字符串长度取一个最小值 然后就水水地过去了~ 30%达成 今天是不是可

【Splay】【启发式合并】hdu6133 Army Formations

题意:给你一颗树,每个结点的儿子数不超过2.每个结点有一个权值,一个结点的代价被定义为将其子树中所有结点的权值放入数组排序后,每个权值乘以其下标的和.让你计算所有结点的代价. 二叉树的条件没有用到. 每个结点开一个Splay,从叶子往上启发式合并上去,可以先bfs一遍确定合并顺序.每一次将Splay大小较小的结点的权值全提取出来塞到较大的里面. 由于权值可能重复出现,所以每个结点记个cnt. 答案统计的时候,就将那个刚塞进去的旋到根,然后答案加上左子树的权值和,再加上(右子树的权值的个数+该结点

数组splay ------ luogu P3369 【模板】普通平衡树(Treap/SBT)

二次联通门 : luogu P3369 [模板]普通平衡树(Treap/SBT) #include <cstdio> #define Max 100005 #define Inline __attri\ bute__( ( optimize( "-O2" ) ) ) Inline void read (int &now) { now = 0; register char word = getchar (); bool temp = false; while (wor

平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】

平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是将有向树的所有边看成无向边形成的树状图.树是一种递归的数据结构,所以我们研究树也是按照递归的方式去研究的. 2.什么是二叉树. 我们给出二叉树的递归定义如下: (1)空树是一个二叉树. (2)单个节点是一个二叉树. (3)如果一棵树中,以它的左右子节点为根形成的子树都是二叉树,那么这棵树本身也是二叉