更新:能过模板题(和加强版)的代码:

普通平衡树:

(请自行实现读入和输出函数)

点击查看代码
#include <iostream>
#include <random>
#include <time.h>
const int MAXN=1e6+50;
std::mt19937 Rand(time(NULL));
int tot;
int Size[MAXN];
int Val[MAXN],Price[MAXN];
int Son[MAXN][2];
int New(int NewVal)
{
Size[++tot]=1;
Val[tot]=NewVal;
Price[tot]=Rand();
return tot;
}
void PushUp(int u)
{
Size[u]=Size[Son[u][0]]+Size[Son[u][1]]+1;
}
void Split(int u,int k,int &Root1,int &Root2)//默认按值小于等于k分裂
{
if(!u)
{
Root1=Root2=0;
return;
}
if(Val[u]<=k)
{
Root1=u;
Split(Son[u][1],k,Son[u][1],Root2);//因为Root1=u了,所以Son[u]也必须在左树内
}
else
{
Root2=u;
Split(Son[u][0],k,Root1,Son[u][0]);//Root2=u,所有Son[u]必须在右树内
}
PushUp(u);
}
int Merge(int x,int y)
{
if(!x||!y)
{
return x|y;
}
if(Price[x]<=Price[y])
{
Son[x][1]=Merge(Son[x][1],y);
PushUp(x);
return x;
}
else
{
Son[y][0]=Merge(x,Son[y][0]);
PushUp(y);
return y;
}
}
int GetKth(int u,int k)//从小到大排名
{
if(Size[u]==1)
return Val[u];
if(Size[Son[u][0]]>=k)
return GetKth(Son[u][0],k);
k-=Size[Son[u][0]];
if(k==1)
return Val[u];
else
return GetKth(Son[u][1],k-1);
}
int GetMax(int u)
{
if(Size[u]==1)
return Val[u];
if(Son[u][1]!=0)
return GetMax(Son[u][1]);
return Val[u];
}
int GetMin(int u)
{
if(Size[u]==1)
return Val[u];
if(Son[u][0]!=0)
return GetMin(Son[u][0]);
return Val[u];
}
int N;
int Root;
int main()
{
read(N);
int opt,x,Root1,Root2,Root3;
while(N--)
{
read(opt);
read(x);
if(opt==1)
{
Split(Root,x,Root1,Root2);
Root=Merge(Merge(Root1,New(x)),Root2);
}
if(opt==2)
{
Split(Root,x,Root2,Root3);
Split(Root2,x-1,Root1,Root2);
Root2=Merge(Son[Root2][0],Son[Root2][1]);
Root=Merge(Merge(Root1,Root2),Root3);
}
if(opt==3)
{
Split(Root,x-1,Root1,Root2);
print(Size[Root1]+1);
putchar('\n');
Root=Merge(Root1,Root2);
}
if(opt==4)
{
print(GetKth(Root,x));
putchar('\n');
}
if(opt==5)
{
Split(Root,x-1,Root1,Root2);
print(GetMax(Root1));
putchar('\n');
Root=Merge(Root1,Root2);
}
if(opt==6)
{
Split(Root,x,Root1,Root2);
print(GetMin(Root2));
putchar('\n');
Root=Merge(Root1,Root2);
}
}
}

文艺平衡树:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e6+50;
mt19937 Rand(time(NULL));
int tot;
int Size[MAXN];
int Val[MAXN],Price[MAXN];
int Son[MAXN][2];
int New(int NewVal)
{
Size[++tot]=1;
Val[tot]=NewVal;
Price[tot]=Rand();
return tot;
}
void PushUp(int u)
{
Size[u]=Size[Son[u][0]]+Size[Son[u][1]]+1;
}
int Reverse[MAXN];
void PushDown(int u)
{
if(Reverse[u])
{
swap(Son[u][0],Son[u][1]);
Reverse[Son[u][0]]^=1;
Reverse[Son[u][1]]^=1;
Reverse[u]=0;
}
}
void Split(int u,int k,int &Root1,int &Root2)
{
if(!u)
{
Root1=Root2=0;
return;
}
PushDown(u);
if(k>Size[Son[u][0]])
{
Root1=u;
Split(Son[u][1],k-Size[Son[u][0]]-1,Son[u][1],Root2);
}
else
{
Root2=u;
Split(Son[u][0],k,Root1,Son[u][0]);
}
PushUp(u);
}
int Merge(int x,int y)
{
if(!x||!y)
{
return x|y;
}
PushDown(x);
PushDown(y);
if(Price[x]<=Price[y])
{
Son[x][1]=Merge(Son[x][1],y);
PushUp(x);
return x;
}
else
{
Son[y][0]=Merge(x,Son[y][0]);
PushUp(y);
return y;
}
}
int GetKth(int u,int k)//从小到大排名
{
PushDown(u);
if(Size[u]==1)
return Val[u];
if(Size[Son[u][0]]>=k)
return GetKth(Son[u][0],k);
k-=Size[Son[u][0]];
if(k==1)
return Val[u];
else
return GetKth(Son[u][1],k-1);
}
int N,M;
int Root;
int main()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
{
Root=Merge(Root,New(i));
}
while(M--)
{
int l,r;
scanf("%d%d",&l,&r);
int Root1,Root2,Root3;
Split(Root,l-1,Root1,Root3);
Split(Root3,r-l+1,Root2,Root3);
Reverse[Root2]^=1;
Root=Merge(Merge(Root1,Root2),Root3);
}
for(int i=1;i<=N;i++)
{
printf("%d ",GetKth(Root,i));
}
}

(过几天改成正经博客)

每个点有权值和随机权值

权值满足二叉搜索树性质

随机权值满足堆性质

Size[i]:i的子树大小

Val[i]:i的值

Price[i]:i的随机权值,rand()

Son[i][0/1]:儿子

merge(x,y):合并以x为根的子树和以y为根的子树

x子树的最大值小于y子树的最小值,即y永远放右子树,x永远放左子树

split(u,k,Root1,Root2):把u按k(权值比k小、比k大 或 中序遍历前k个值、后面的值)分成左Root1,右Root2两棵树

考虑分成权值比k小,对于当前的u, Val[u]<=k,u和左子树全都给Root1,递归到右子树,反之亦然

对于插入,删除,把整棵树分成左,输入的Val(需要插入或删除)为根的那棵树,右,即先分成比Val小和比Val大,如果插入就新建一个节点然后跟左右2个区间merge,如果删除就再把比Val小的树split成比Val-1小的树和为Val的树 ,因为删除只需要删除一个(可能有多个值),所以把=Val的这棵树的根左右儿子合并起来再把这3个区间合并

第k大:常规查

Val是第几大:split成小于(因为相同不算)Val的树和大于等于Val的树,rank为小于Val的树的Size+1

Val的前驱:split成<=Val的树和>Val的树,找<=Val的树的最大值(一直右走)

后继:同理

每次split和merge都要pushup:Size、其它要维护的东西

区间翻转:打个标记,每次要递归的时候判断当前点有没有标记,有就下传,然后交换左右儿子。

mt19937:好用的随机数

mt19937 Rand(time(NULL));

做了题才发现,平衡树只能维护一个维度。比如,我想维护区间翻转,就得维护下标,从而没办法查第k 大,但是能查下标为 k 的数,反之也是如此。

再深入一下——所有数据结构都是维护一个维度,但一堆数据结构合起来,就能维护很多维度了。比如,普通树状数组可以维护一个维度的信息,CDQ 可以维护一个维度的信息,最开始再排个序,消除一个维度的影响,就可以做三维偏序了。

时间复杂度跟所需要维护的信息量有关,信息量是固定的,而最少都需要一个 log 来维护,就是跟计算信息熵的那个 log 一个意思。

自学FHQ-treap的草稿的更多相关文章

  1. fhq treap最终模板

    新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...

  2. NOI 2002 营业额统计 (splay or fhq treap)

    Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每 ...

  3. 【POJ2761】【fhq treap】A Simple Problem with Integers

    Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...

  4. 【fhq Treap】bzoj1500(听说此题多码上几遍就能不惧任何平衡树题)

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 15112  Solved: 4996[Submit][Statu ...

  5. 「FHQ Treap」学习笔记

    话说天下大事,就像fhq treap —— 分久必合,合久必分 简单讲一讲.非旋treap主要依靠分裂和合并来实现操作.(递归,不维护fa不维护cnt) 合并的前提是两棵树的权值满足一边的最大的比另一 ...

  6. FHQ Treap摘要

    原理 以随机数维护平衡,使树高期望为logn级别 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 因此可持久化 先介绍变量 ; int n; struct Node { int v ...

  7. FHQ Treap小结(神级数据结构!)

    首先说一下, 这个东西可以搞一切bst,treap,splay所能搞的东西 pre 今天心血来潮, 想搞一搞平衡树, 先百度了一下平衡树,发现正宗的平衡树写法应该是在二叉查找树的基础上加什么左左左右右 ...

  8. 在平衡树的海洋中畅游(四)——FHQ Treap

    Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...

  9. 浅谈fhq treap

    一.简介 fhq treap 与一般的treap主要有3点不同 1.不用旋转 2.以merge和split为核心操作,通过它们的组合实现平衡树的所有操作 3.可以可持久化 二.核心操作 代码中val表 ...

  10. fhq treap 学习笔记

    序 今天心血来潮,来学习一下fhq treap(其实原因是本校有个OIer名叫fh,当然不是我) 简介 fhq treap 学名好像是"非旋转式treap及可持久化"...听上去怪 ...

随机推荐

  1. 小笨自采集在线电脑壁纸 v2.0-支持移动端自适应,支持https

    这周闲着没事,翻着网站,记得去年的发表的一篇文章小笨分享一款高清电脑壁纸API,主要是将孟坤壁纸改造支持https,还加了一个搜索功能.以前的壁纸是这样的: 但是,不支持移动端浏览,于是小笨趁着这周周 ...

  2. 易基因:PIWI/piRNA在人癌症中的表观遗传调控机制(DNA甲基化+m6A+组蛋白修饰)|综述

    大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因. 2023年03月07日,南华大学衡阳医学院李二毛团队在<Molecular Cancer>杂志发表了题为"The ...

  3. java多线程基础小白指南--关键字识别(start,run,sleep,wait,join,yield)

    在学习java多线程基础上,会遇到几个关键字,理解并识别它们是掌握多线程的必备知识,下面,我将通过源码或者程序演示给出我对这几个关键字的理解,如果有不同意见,欢迎在评论区或者发私信与我探讨. 一.st ...

  4. Linux理论知识

    Linux理论知识   理论知识 1.1文件名后缀 1 作用是说明和注释一个文件的性质. 2 与文件类型无关. 1.2常见的压缩文件后缀名 1.gz 2.bz2 3.xz 4.zip 5.tar 6. ...

  5. Unity3D中的Attribute详解(三)

    上一篇我们对系统的Attributes进行了MSIL代码的查看,了解到了其本质就是一个类的构造函数.本章我们将编写自己的Attributes. 首先我们定义书的属性代码,如下: [AttributeU ...

  6. [Java] 多线程系列之Fork/Join框架[转载]

    1 工作原理 1.1 核心思想:分而治之 & 并行执行 Fork/Join框架是Java 7提供的一个用于并行执行任务的框架, 核心思想就是把大任务分割成若干个小任务,最终汇总每个小任务结果后 ...

  7. [Java EE]缓存技术初探

    1 背景 使用场景:计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存. 高并发下,为提高 频繁 查询 大量 可能常用的 数据库数据的 查询效率. 大部分情况下, ...

  8. day96:flask:flask-migrate&flask-session&蓝图Blueprint&蓝图的运行机制&基于flask仿照django进行项目架构

    目录 1.flask-migrate 2.flask-session 3.蓝图:Blueprint 4.蓝图的运行机制 5.基于flask仿照django进行项目架构 1.准备工作 2.加载配置文件 ...

  9. Ubuntu20.04 Docker搭建远程xfce桌面以及ssh教程

    简介:本文主要介绍ubuntu20.04容器中搭建xfce远程桌面.C++.Go环境.容器内docker操作配置. 一.创建容器1.创建容器 docker pull ubuntu:20.04docke ...

  10. SSH: Linux开启ssh并启动root登录设置默认密码

    apt update && apt install -y openssh-server echo "PermitRootLogin yes" >> /e ...