fhqtreap入门

介绍

fhqtreap为利用分裂和合并来满足平衡树的性质,不需要旋转操作的一种平衡树。
并且利用函数式编程可以极大的简化代码量。

核心操作

(均为按位置分裂合并)

struct fhq {
    int lc, rc, siz, rnd, val;
        //lc为左子树,rc为右子树,siz为子树大小(位置分裂即按siz分裂),rnd为随机值,val为该节点储存的值
}t[N];
#define lc (t[rt].lc)
#define rc (t[rt].rc)
//下方用到的宏定义

split(rt,l,r,k) 把一个根为rt的子树split成一个根为l和一个根为r的子树(以第k大为界限)

void split(int rt, int &l, int &r, int k) {
    if(!k) l = 0, r = rt;
    else if(t[rt].siz == k) l = rt, r = 0;
    else if(t[lc].siz <= k) l = rt, split(lc, l, lc, k), up(rt);
    else r = rt, split(rc, rc, r, k - t[lc].siz - 1), up(rt);
}

merge(rt,l,r) 把根为l和根为r的子树merge成一个根为rt的子树
merge默认子树l的权值比子树r的权值小
merge满足小根堆性质(对rnd)

void merge(int &rt, int l, int r) {
    if(!l || !r) rt = l + r;
    else if(t[l].rnd < t[r].rnd) rt = l, merge(rc, rc, r), up(rt);
    else rt = r, merge(lc, l, lc), up(rt);
}

fhqtreap也有一种按权值分裂的做法,但用处不大,如果要用位置分裂实现权值分裂,可以将序列构造成一个递增的序列,写一个rank求一下插入的数的在序列中的位置,插入到那里就行了(这样就能搞权值分裂能搞的东西了)、
区间操作打懒标记的话,在split和merge的时候下传即可。

常用操作

里面的sum即为上方提到的val。
建新节点

int build(int val) {
    t[++tot].rnd = rand() << 15 | rand();
    t[tot].siz = 1;
    t[tot].sum = val;
    return tot;
}

查排名(上方提到的rank,这个是\(logn\)的)

int rank(int rt, int val) {
    if(!rt) return 0;
    if(t[rt].sum >= val) return rank(lc, val);
    return rank(rc, val) + t[lc].siz + 1;
}

插入

inline void insert(int val) {
    int rk = rank(root, val), x, y;
    split(root, x, y, rk);
    int z = build(val);
    merge(x, x, z); merge(root, x, y);
}

删除

inline void del(int val) {
    int rk = rank(root, val) + 1, x, y, z;
    split(root, x, y, rk);
    split(x, x, z, rk - 1);
    merge(root, x, y);
}

其他操作均类似。
要注意的一个问题:merge和split均针对的是根为rt的子树,所以对应的k也是他们子树中的第k大。可以看看下面的代码。

inline int find(int rk) {//找排名为rk的数
    int x, y, z, ans;
    split(root, x, y, rk - 1);
    split(y, y, z, 1);
    ans = t[y].sum;
    merge(y, y, z); merge(root, x, y);
    return ans;
    /*
    split(root, x, y, rk); split(x, z, x, rk - 1);
    ans = t[x].sum;
    merge(x, z, x), merge(root, x, y);
    return ans;
    */
}

即在一整个根为root的树中找第rk大,可以有两种实现:1.分裂成前rk大和剩下的,并将前rk大分裂成rk-1大和第rk大。2.分裂成前rk-1大和剩下的,将剩下的那部分,分裂成第一大(不要弄成rk+1!)和后面的

模板题

LuoguP3369 【模板】普通平衡树
就是平衡树的常见操作,可以直接写权值分裂,也可以写位置分裂(构造成递增数列)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <queue>
#include <ctime>
#include <cmath>
#include <stack>
#include <deque>
#include <map>
#include <set>

#define ll long long
const int inf = 2e9 + 10;
typedef unsigned long long ull;

namespace io {

#define in(a) a = read()
#define out(a) write(a)
#define outn(a) out(a), putchar('\n')

#define I_int int
inline I_int read() {
    I_int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}
char F[200];
inline void write(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
}
#undef I_int

}
using namespace io;

using namespace std;

#define N 500010

#define lc (t[rt].l)
#define rc (t[rt].r)
struct fhq {
    int l, r, sum, siz, rnd;
} t[N];
int tot = 0, root = 0;
void up(int rt) {
    if(!rt) return;
    t[rt].siz = 1 + t[lc].siz + t[rc].siz;
}
void split(int rt, int &l, int &r, int k) {
    //把根为rt的子树,以第k个为界限split成两个子树
    //第k个可以是位置,也可以是权值
    //这里的k是位置
    if(!k) l = 0, r = rt;
    else if(k == t[rt].siz) l = rt, r = 0;
    else if(k <= t[lc].siz) r = rt, split(lc, l, lc, k), up(rt);
    else l = rt, split(rc, rc, r, k - t[lc].siz - 1), up(rt);
}
void merge(int &rt, int l, int r) {
    //把l子树和r子树merge为一棵根为rt的子树
    if(!l || !r) rt = l + r;
    else if(t[l].rnd < t[r].rnd) rt = l, merge(t[rt].r, t[rt].r, r), up(rt);
    else rt = r, merge(t[rt].l, l, t[rt].l), up(rt);
}
int build(int val) {
    t[++tot].rnd = rand() << 15 | rand();
    t[tot].siz = 1;
    t[tot].sum = val;
    return tot;
}
int rank(int rt, int val) {
    if(!rt) return 0;
    if(t[rt].sum >= val) return rank(lc, val);
    return rank(rc, val) + t[lc].siz + 1;
}
inline void insert(int val) {
    int rk = rank(root, val), x, y;
    split(root, x, y, rk);
    int z = build(val);
    merge(x, x, z); merge(root, x, y);
}
inline void del(int val) {
    int rk = rank(root, val) + 1, x, y, z;
    split(root, x, y, rk);
    split(x, x, z, rk - 1);
    merge(root, x, y);
}
inline int find(int rk) {
    int x, y, z, ans;
    split(root, x, y, rk - 1);
    split(y, y, z, 1);
    ans = t[y].sum;
    merge(y, y, z); merge(root, x, y);
    return ans;
    /*
    split(root, x, y, rk); split(x, z, x, rk - 1);
    ans = t[x].sum;
    merge(x, z, x), merge(root, x, y);
    return ans;
    */
}
inline int pre(int val) {
    int x, y, z, ans, rk = rank(root, val);
    split(root, x, y, rk);
    split(x, z, x, rk - 1);
    ans = t[x].sum;
    merge(x, z, x); merge(root, x, y);
    return ans;
}
inline int succ(int val) {
    int x, y, z, ans, rk = rank(root, val + 1);
    split(root, x, y, rk + 1); split(x, z, x, rk);
    ans = t[x].sum;
    merge(x, z, x); merge(root, x, y);
    return ans;
}
int main() {
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin);
#endif
    srand((unsigned)time(0));
    t[0].rnd = t[0].sum = inf;
    int n = read();
    for(int i = 1; i <= n; ++i) {
        int op = read(), x = read();
        if(op == 1) insert(x);
        if(op == 2) del(x);
        if(op == 3) printf("%d\n", rank(root, x) + 1);
        if(op == 4) printf("%d\n", find(x));
    if(op == 5) printf("%d\n", pre(x));
    if(op == 6) printf("%d\n", succ(x));
    }
}

原文地址:https://www.cnblogs.com/henry-1202/p/10464231.html

时间: 2024-10-12 01:26:55

fhqtreap入门的相关文章

R语言快速上手入门

R语言快速上手入门 课程学习网址:http://www.xuetuwuyou.com/course/196 课程出自学途无忧网:http://www.xuetuwuyou.com 课程简介 本教程深入浅出地讲解如何使用R语言玩转数据.课程中涵盖R语言编程的方方面面,内容涉及R对象的类型.R的记号体系和环境系统.自定义函数.if else语句.for循环.S3类R的包系统以及调试工具等.本课程还通过示例演示如何进行向量化编程,从而对代码进行提速并尽可能地发挥R的潜能.本课程适合立志成为数据科学家的

笔记:Spring Cloud Zuul 快速入门

Spring Cloud Zuul 实现了路由规则与实例的维护问题,通过 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获取了所有其他微服务的实例信息,这样的设计非常巧妙的将服务治理体系中维护的实例信息利用起来,使得维护服务实例的工作交给了服务治理框架自动完成,而对路由规则的维护,默认会将通过以服务名作为 ContextPath 的方式来创建路由映射,也可以做一些特别的配置,对于签名校验.登录校验等在微服务架构中的冗余问题

linux入门基础知识及简单命令介绍

linux入门基础知识介绍 1.计算机硬件组成介绍 计算机主要由cpu(运算器.控制器),内存,I/O,外部存储等构成. cpu主要是用来对二进制数据进行运算操作,它从内存中取出数据,然后进行相应的运算操作.不能从硬盘中直接取数据. 内存从外部存储中取出数据供cpu运存.内存的最小单位是字节(byte) 备注:由于32的cpu逻辑寻址能力最大为32内存单元.因此32位cpu可以访问的最大内存空间为:4GB,算法如下: 2^32=2^10*2^10*2^10*2^2 =1024*1024*1024

JAVA通信系列二:mina入门总结

一.学习资料 Mina入门实例(一) http://www.cnblogs.com/juepei/p/3939119.html Mina入门教程(二)----Spring4 集成Mina http://www.cnblogs.com/juepei/p/3940396.html Apache Mina 入门实例--创建一个MINA时间服务http://loftor.com/archives/apache-mina-quick-start-guide.html MINA2.0用户手册中文版--系列文

Storm入门(四)WordCount示例

Storm API文档网址如下: http://storm.apache.org/releases/current/javadocs/index.html 一.关联代码 使用maven,代码如下. pom.xml  和Storm入门(三)HelloWorld示例相同 RandomSentenceSpout.java /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor lice

浅谈Ubuntu PowerShell——小白入门教程

早在去年八月份PowerShell就开始开源跨平台了,但是一直没有去尝试,叫做PowerShell Core. 这里打算简单介绍一下如何安装和简单使用,为还不知道PowerShell Core on Ubuntu的同学们提供一点小小的入门帮助,谢谢大家支持~ PowerShell Core是由Microsoft开发的运行在.Net Core上的开源跨平台的任务自动化和配置管理系统. 1.   在Ubuntu 16.04上安装PowerShell Core a)         导入公共存储库GP

2.vue.js 入门环境搭建

原文链接:http://blog.csdn.net/luckylqh/article/details/52863026?locationNum=2&fps=1 vue这个新的工具,确实能够提高效率,在经历的一段时间的摧残之后,终于能够有一个系统的认识了,下面就今天的收获做一个总结,也是vue入门的精髓: 1.要使用vue来开发前端框架,首先要有环境,这个环境要借助于node,所以要先安装node,借助于node里面的npm来安装需要的依赖等等. 这里有一个小技巧:如果在cmd中直接使用npm来安

学习mysql的笔记:mysql十大基本入门语句

学习mysql数据库,从最简单的十条入门语句开始.刚开始学习mysql,老师让我们用cmd控制台输入语句,而不是选择用工具输入,这是为了我们能够先熟悉mysql语句. 首先要先进入mysql,语句为:mysql -hlocalhost -uroot -p  然后回车,输入密码. C:\Users\Administrator>mysql -hlocalhost -uroot -p Enter password: ****** 成功进入的话会出现 Welcome to the MySQL monit

Java - Java入门(2-1am)

第一讲.Java入门 1. 计算机语言是人和计算机进行交互的一种工具,人们通过使用计算机语言编写程序来向计算机施令,计算机则执行程序,并把结果输出给用户. 2. 机器语言:由0.1序列构成的指令码组成 如:10000000  加        10010000减          11110000 打印 3. 汇编语言:用助记符号描述的指令系统 如: Mov   A, #10; Mov   B, #20; Add   A,  B; 4. 高级语言: 面向过程的高级语言:程序设计的基本单元为函数