NOIP回来就一直想着学平衡树。。。平衡树写久了调不出来真的会头脑发热.jpg

  大概只写了几道题。。。

  fhqtreap是不需要旋转的平衡树,仅使用分裂合并,一样可以保持平衡树的性质,并且可以非常简单地处理区间问题。

  fhqtreap的核心有两段代码,split(分裂)和merge(合并)

  split(x, l, r, k),表示把原x的子树以第k小数为界限,权值<=第k小数的数分在左子树,根为l,其他的分在右子树,根为r

void split(int x, int &l, int &r, int k)
{
if(!k) l=, r=x;
else if(k==tree[x].size) l=x, r=;
else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x);
else down(l=x), split(rt, rt, r, k-tree[lt].size-), up(x);
}

  merge(x, l, r),表示把根为l的子树和根为r的子树合并成一棵根为x的子树

void merge(int &x, int l, int r)
{
if(!l || !r) x=l+r;
else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x);
else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x);
}

  fhqtreap能实现哪些操作呢?

  单点/区间插入,单点/区间删除,区间加,区间查询和,区间查询最值,区间反转,区间旋(xun)转(jun)(其实就是这个区间整体后移k步,超过区间的补到前面),还有等等...splay和线段树能做的大部分都能够做到...并且常数比splay小的多...其实写的丑的话被splay吊打,而且要写的优美可能每个操作都得单独写一个子程序...

  查询一个区间[l, r]就把一棵树split成三棵树,查中间那棵,再把它们merge回去

核心代码:

inline void add(int l, int r, int delta)//任意操作
{
int x, y, z;
split(root, x, y, r); split(x, z, x, l-);//拆成z,x,y三棵
addone(x, delta);//任意操作
merge(x, z, x); merge(root, x, y);//并回去
}

  随机数可以用rand()<<15|rand()来求

  初始最好tree[0].rnd=tree[0].sum=tree[0].xxx=...=inf,否则up的时候可能求min会GG,同理求max要赋值-inf

例题时间~

例1 bzoj 3224 tyvj 1729

  经典题。。。需要多运用一个rank查询x数的排名,才能进行split(否则得另写一个按数字分的split,相比起来这样更好写)

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
#define lt tree[x].ls
#define rt tree[x].rs
using namespace std;
const int maxn=, inf=1e9+;
int n, m, x, y, z, root, tott, tmp, opt;
struct treap{int rnd, sum, size, ls, rs;} tree[maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-'&&(f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline void build(int &x, int delta)
{
tree[x=++tott].rnd=rand()<<|rand();
tree[x].sum=delta;
tree[x].size=;
}
inline void up(int x) {if(!x) return; tree[x].size=tree[lt].size+tree[rt].size+;}
void merge(int &x, int l, int r)
{
if(!l || !r) x=l+r;
else if(tree[l].rnd<tree[r].rnd) x=l, merge(tree[x].rs, tree[x].rs, r), up(x);
else x=r, merge(tree[x].ls, l, tree[x].ls), up(x);
}
void split(int x, int &l, int &r, int k)
{
if(!k) l=, r=x;
else if(k==tree[x].size) l=x, r=;
else if(k<=tree[lt].size) r=x, split(lt, l, lt, k), up(x);
else l=x, split(rt, rt, r, k-tree[lt].size-), up(x);
}
int rank(int x, int w)
{
if(!x) return ;
if(tree[x].sum>=w) return rank(lt, w);
return rank(rt, w)+tree[lt].size+;
}
inline void insert(int delta)
{
int x, y, rk=rank(root, delta);
split(root, x, y, rk);
build(tmp, delta);
merge(x, x, tmp); merge(root, x, y);
}
inline void del(int delta)
{
int x, y, z, rk=rank(root, delta)+;
split(root, x, y, rk); split(x, x, z, rk-);
merge(root, x, y);
}
inline int find(int delta)
{
int x, y, z, ans;
split(root, x, y, delta); split(x, z, x, delta-);
ans=tree[x].sum;
merge(x, z, x); merge(root, x, y);
return ans;
}
inline int pre(int delta)
{
int x, y, z, ans, rk=rank(root, delta);
split(root, x, y, rk); split(x, z, x, rk-);
ans=tree[x].sum;
merge(x, z, x); merge(root, x, y);
return ans;
}
inline int succ(int delta)
{
int x, y, z, ans, rk=rank(root, delta+);
split(root, x, y, rk+); split(x, z, x, rk);
ans=tree[x].sum;
merge(x, z, x); merge(root, x, y);
return ans;
}
int main()
{
srand(); read(n); tree[].rnd=tree[].sum=inf;
for(int i=;i<=n;i++)
{
read(opt); read(x);
if(opt==) insert(x);
else if(opt==) del(x);
else if(opt==) printf("%d\n", rank(root, x)+);
else if(opt==) printf("%d\n", find(x));
else if(opt==) printf("%d\n", pre(x));
else printf("%d\n", succ(x));
}
}

例2 poj 3580

  整合了大部分操作。。区间加,区间反转,区间旋(xun)转(jun),单点插入,单点删除,区间查询最小值

  区间反转打标记就好了,down的时候交换左右子树,给左右子树打上标记即可,打标记的方式是^=1

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
#define lt tree[x].ls
#define rt tree[x].rs
using namespace std;
const int maxn=, inf=1e9+;
int n, m, x, y, z, root, tott, tmp;
struct treap{int rnd, mn, sum, delta, rev, size, ls, rs;} tree[maxn];
char s[];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-'&&(f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline int min(int a, int b){return a<b?a:b;}
inline void addone(int x, int delta) {if(!x) return; tree[x].sum+=delta; tree[x].delta+=delta; tree[x].mn+=delta;}
inline void reverseone(int x) {tree[x].rev^=; swap(lt, rt);}
inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<|rand(); tree[x].sum=tree[x].mn=delta; tree[x].size=;}
inline void up(int x)
{
if(!x) return;
tree[x].mn=min(tree[x].sum, min(tree[lt].mn, tree[rt].mn));
tree[x].size=tree[lt].size+tree[rt].size+;
}
inline void down(int x)
{
if(!x) return;
if(tree[x].delta) addone(lt, tree[x].delta), addone(rt, tree[x].delta);
if(tree[x].rev) reverseone(lt), reverseone(rt);
tree[x].delta=tree[x].rev=;
}
void merge(int &x, int l, int r)
{
if(!l || !r) x=l+r;
else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x);
else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x);
}
void split(int x, int &l, int &r, int k)
{
if(!k) l=, r=x;
else if(k==tree[x].size) l=x, r=;
else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x);
else down(l=x), split(rt, rt, r, k-tree[lt].size-), up(x);
}
inline void insert(int pos, int delta)
{
int x, y;
split(root, x, y, pos);
merge(x, x, delta); merge(root, x, y);
}
inline void del(int pos)
{
int x, y, z;
split(root, x, y, pos); split(x, x, z, pos-);
merge(root, x, y);
}
inline void add(int l, int r, int delta)
{
int x, y, z;
split(root, x, y, r); split(x, z, x, l-);
addone(x, delta);
merge(x, z, x); merge(root, x, y);
}
inline void reverse(int l, int r)
{
int x, y, z;
split(root, x, y, r); split(x, z, x, l-);
reverseone(x);
merge(x, z, x); merge(root, x, y);
}
inline void revolve(int l, int r, int delta)
{
int x, y, z, h;
split(root, x, y, r-delta); split(x, z, x, l-); split(y, y, h, delta);
merge(x, y, x); merge(x, z, x); merge(root, x, h);
}
inline int query(int l, int r)
{
int x, y, z, ans;
split(root, x, y, r); split(x, z, x, l-);
ans=tree[x].mn;
merge(x, z, x); merge(root, x, y);
return ans;
}
int main()
{
srand(); read(n); tree[].rnd=tree[].mn=tree[].sum=inf;
for(int i=;i<=n;i++) read(x), build(tmp, x), merge(root, root, tmp);
read(m);
for(int i=;i<=m;i++)
{
scanf("%s", s+);
if(s[]=='A') read(x), read(y), read(z), add(x, y, z);
else if(s[]=='R' && s[]=='O') read(x), read(y), read(z), revolve(x, y, z%(y-x+));
else if(s[]=='R' && s[]=='E') read(x), read(y), reverse(x, y);
else if(s[]=='D') read(x), del(x);
else if(s[]=='I') read(x), read(y), build(tmp, y), insert(x, tmp);
else read(x), read(y), printf("%d\n", query(x, y));
}
}

例3 bzoj 1251

  除了最小值变最大值之外,操作是poj 3580的子集。。。直接搬一下就好。。。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define lt tree[x].ls
#define rt tree[x].rs
using namespace std;
const int maxn=, inf=1e9+;
int n, m, ty, x, y, z, root, tott;
struct treap{int rnd, mx, sum, delta, rev, size, ls, rs;} tree[maxn];
char buf[],*ptr=buf-;
inline int read()
{
char c=*++ptr; int s=,t=;
while(c<||c>) t=-, c=*++ptr;
while(c>=&&c<=) s=s*+c-'', c=*++ptr;
return s*t;
}
inline int max(int a, int b){return a>b?a:b;}
inline void addone(int x, int delta) {tree[x].sum+=delta; tree[x].delta+=delta; tree[x].mx+=delta;}
inline void reverseone(int x) {tree[x].rev^=; swap(lt, rt);}
inline void up(int x)
{
if(!x) return;
tree[x].mx=max(tree[x].sum, max(tree[lt].mx, tree[rt].mx));
tree[x].size=tree[lt].size+tree[rt].size+;
}
inline void down(int x)
{
if(!x) return;
if(tree[x].delta) addone(lt, tree[x].delta), addone(rt, tree[x].delta);
if(tree[x].rev) reverseone(lt), reverseone(rt);
tree[x].delta=tree[x].rev=;
}
void merge(int &x, int l, int r)
{
if(!l || !r) x=l+r;
else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x);
else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x);
}
void split(int x, int &l, int &r, int k)
{
if(!k) l=, r=x;
else if(k==tree[x].size) l=x, r=;
else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x);
else down(l=x), split(rt, rt, r, k-tree[lt].size-), up(x);
}
inline void add(int l, int r, int delta)
{
int x, y, z;
split(root, x, y, r); split(x, z, x, l-);
addone(x, delta);
merge(x, z, x); merge(root, x, y);
}
inline void reverse(int l, int r)
{
int x, y, z;
split(root, x, y, r); split(x, z, x, l-);
reverseone(x);
merge(x, z, x); merge(root, x, y);
}
inline int query(int l, int r)
{
int x, y, z, ans;
split(root, x, y, r); split(x, z, x, l-);
ans=tree[x].mx;
merge(x, z, x); merge(root, x, y);
return ans;
}
int main()
{
fread(buf,,sizeof(buf),stdin); n=read(); m=read(); tree[].rnd=inf; tree[].mx=tree[].sum=-inf;
for(int i=;i<=n;i++) tree[++tott].size=, tree[tott].rnd=rand()<<|rand(), merge(root, root, tott);
for(int i=;i<=m;i++)
{
ty=read(); x=read(); y=read();
if(ty==) z=read(), add(x, y, z);
else if(ty==) reverse(x, y);
else printf("%d\n", query(x, y));
}
}

例4 bzoj 3223

  陶冶身心的水题。。。区间反转一个操作而已。。233

                        #include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
#define lt tree[x].ls
#define rt tree[x].rs
using namespace std;
const int maxn=, inf=1e9+;
int n, m, x, y, z, root, tott, tmp;
struct treap{int rnd, sum, rev, size, ls, rs;} tree[maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-'&&(f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline void reverseone(int x) {tree[x].rev^=; swap(lt, rt);}
inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<|rand(); tree[x].sum=delta; tree[x].size=;}
inline void up(int x) {if(!x) return; tree[x].size=tree[lt].size+tree[rt].size+;}
inline void down(int x)
{
if(!x) return;
if(tree[x].rev) reverseone(lt), reverseone(rt);
tree[x].rev=;
}
void merge(int &x, int l, int r)
{
if(!l || !r) x=l+r;
else if(tree[l].rnd<tree[r].rnd) down(x=l), merge(tree[x].rs, tree[x].rs, r), up(x);
else down(x=r), merge(tree[x].ls, l, tree[x].ls), up(x);
}
void split(int x, int &l, int &r, int k)
{
if(!k) l=, r=x;
else if(k==tree[x].size) l=x, r=;
else if(k<=tree[lt].size) down(r=x), split(lt, l, lt, k), up(x);
else down(l=x), split(rt, rt, r, k-tree[lt].size-), up(x);
}
inline void reverse(int l, int r)
{
int x, y, z;
split(root, x, y, r); split(x, z, x, l-);
reverseone(x);
merge(x, z, x); merge(root, x, y);
}
void print(int x)
{
if(!x) return; down(x);
print(lt); printf("%d ", tree[x].sum); print(rt);
}
int main()
{
srand(); read(n); read(m); tree[].rnd=tree[].sum=inf;
for(int i=;i<=n;i++) build(tmp, i), merge(root, root, tmp);
for(int i=;i<=m;i++) read(x), read(y), reverse(x, y);
print(root);
}

例5 bzoj 1500

    ...大boss,头皮发麻过几天再补,需要垃圾回收,线性建树,让我冷静一下...

    博主已经咕了(捂脸

【算法】fhqtreap初探的更多相关文章

  1. 图论基础之Dijkstra算法的初探

         图论,顾名思义就是有图有论.        图:由点"Vertex"和边"Edge "组成,且图分为有向图和无向图(本文讨论有向图),之前做毕业设计的 ...

  2. fhqtreap初探

    介绍 fhqtreap为利用分裂和合并来满足平衡树的性质,不需要旋转操作的一种平衡树. 并且利用函数式编程可以极大的简化代码量. (题目是抄唐神的来着) 核心操作 (均为按位置分裂合并) struct ...

  3. 强连通分量算法·$tarjan$初探

    嗯,今天好不容易把鸽了好久的缩点给弄完了--感觉好像--很简单? 算法的目的,其实就是在有向图上,把一个强连通分量缩成一个点--然后我们再对此搞搞事情,\(over\) 哦对,时间复杂度很显然是\(\ ...

  4. Sequential Minimal Optimization(SMO,序列最小优化算法)初探

    什么是SVM SVM是Support Vector Machine(支持向量机)的英文缩写,是上世纪九十年代兴起的一种机器学习算法,在目前神经网络大行其道的情况下依然保持着生命力.有人说现在是神经网络 ...

  5. 深度强化学习day01初探强化学习

    深度强化学习 基本概念 强化学习 强化学习(Reinforcement Learning)是机器学习的一个重要的分支,主要用来解决连续决策的问题.强化学习可以在复杂的.不确定的环境中学习如何实现我们设 ...

  6. 洛谷日报 & 原来博客(转载)

    震惊,新的功能:可以按Ctrl + F 进行关键字查询. \(update\) on 10.26:把这两个月的日报也加入进去了,并且修复了几个错误. 本文会把小编用过的博客和比较好的博客放在这里. 可 ...

  7. Luogu Daily & Original Blog (reproduced)

    震惊,新的功能:可以按Ctrl + F 进行关键字查询. \(update\) on 10.26:把这两个月的日报也加入进去了,并且修复了几个错误. 本文会把小编用过的博客和比较好的博客放在这里. 可 ...

  8. 算法初探:Tensorflow及PAI平台的使用

    前言 Tensorflow这个词由来已久,但是对它的理解一直就停留在“听过”的层面.之前做过一个无线图片适配问题智能识别的项目,基于Tensorflow实现了GoogLeNet - Inception ...

  9. Tarjan算法初探(3):求割点与桥以及双连通分量

    接上一节Tarjan算法初探(2):缩点 在此首先提出几个概念: 割点集合:一个无向连通图G 若删除它的一个点集 以及点集中所有点相连的边(任意一端在点集中)后 G中有点之间不再连通则称这个点集是它的 ...

随机推荐

  1. Python抓取豆瓣《白夜追凶》的评论并且分词

    最近网剧<白夜追凶>在很多朋友的推荐下,开启了追剧模式,自从琅琊榜过后没有看过国产剧了,此剧确实是良心剧呀!一直追下去,十一最后两天闲来无事就抓取豆瓣的评论看一下 相关代码提交到githu ...

  2. 第二十二篇:C++中的多态机制

    前言 封装性,继承性,多态性是面向对象语言的三大特性.其中封装,继承好理解,而多态的概念让许多初学者感到困惑.本文将讲述C++中多态的概念以及多态的实现机制. 什么是多态? 多态就是多种形态,就是许多 ...

  3. SQL Server 还原错误“restore database正在异常终止 错误 3154”

    今天在还原数据库时,先建立相同名字的数据库,然后在该数据库上右键还原数据库.遇到了这样的一个错误: “备份集中的数据库备份与现有的 'RM_DB' 数据库不同. RESTORE DATABASE 正在 ...

  4. sql duplicate key

    本文来自:高爽,转载请注明. 向数据库插入记录时,有时会有这种需求,当符合某种条件的数据存在时,去修改它,不存在时,则新增,也就是saveOrUpdate操作.这种控制可以放在业务层,也可以放在数据库 ...

  5. Win7系统安装 MySQL 8.0.11

    1. 下载 MySQL 8.0.11 版本 下载地址: https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.11-winx64.zip 2. 下载 ...

  6. dfs_部分和问题

    给定整数a1,a2,....,an ,选若干数时它们的和为k. 解析:每个数有两种状态:加.不加. 全部n个数都决定其状态后进行判断.复杂度O(2n) 生成可行解空间多用dfs实现. import j ...

  7. 第六课作业——主从复制和sentinel高可用

    第六课时作业 静哥 by 2016.3.21~2016.4.3   [作业描述] 1.配置主从复制,截图看日志 2.配置一个哨兵,一主一从结构,并实现主宕机从接管的过程,截图显示 3.总结哨兵的原理 ...

  8. TFS二次开发-基线文件管理器(4)-标签的创建

    上一节已经完成了源码文件的读取,如果要将已经选择的文件保存为一个标签(Lable).在VS我们只能对一个目录做标签,非常的不方便.如果用下面的方法,将选择的文件路径保存为一个List在打标签,就非常的 ...

  9. Delphi重定义的消息结构

    // 除去DDE和MDI消息,一共159个消息,其中部分消息仅仅的转定义 // 普通消息,有两个参数和结果 PMessage = ^TMessage; TMessage = packed record ...

  10. 三、Nuxt项目目录结构

    使用IDE打开我们初始化完的新项目,然后发现目录如下图所示 现在来介绍一下每个目录和文件 .idea           是我使用的IDE是IDEA自动生成的,跟项目无关 .nuxt          ...