【算法】fhqtreap初探

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

  大概只写了几道题。。。

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

  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=0, r=x;
    else if(k==tree[x].size) l=x, r=0;
    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-1), 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小的多...

  查询一个区间[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-1);//拆成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 3223 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=500010, inf=1e9+1;
int n, m, x, y, z, root, tott, tmp;
struct treap{int rnd, mn, sum, delta, rev, size, ls, rs;} tree[maxn];
char s[20];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar();
    while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, 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^=1; swap(lt, rt);}
inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<15|rand(); tree[x].sum=tree[x].mn=delta; tree[x].size=1;}
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+1;
}
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=0;
}
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=0, r=x;
    else if(k==tree[x].size) l=x, r=0;
    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-1), 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-1);
    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-1);
    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-1);
    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-1); 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-1);
    ans=tree[x].mn;
    merge(x, z, x); merge(root, x, y);
    return ans;
}
int main()
{
    srand(19260817); read(n); tree[0].rnd=tree[0].mn=tree[0].sum=inf;
    for(int i=1;i<=n;i++) read(x), build(tmp, x), merge(root, root, tmp);
    read(m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s", s+1);
        if(s[1]==‘A‘) read(x), read(y), read(z), add(x, y, z);
        else if(s[1]==‘R‘ && s[4]==‘O‘) read(x), read(y), read(z), revolve(x, y, z%(y-x+1));
        else if(s[1]==‘R‘ && s[4]==‘E‘) read(x), read(y), reverse(x, y);
        else if(s[1]==‘D‘) read(x), del(x);
        else if(s[1]==‘I‘) read(x), read(y), build(tmp, y), insert(x, tmp);
        else read(x), read(y), printf("%d\n", query(x, y));
    }
}

  例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=500010, inf=1e9+1;
int n, m, x, y, z, root, tott, tmp;
struct treap{int rnd, mn, sum, delta, rev, size, ls, rs;} tree[maxn];
char s[20];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar();
    while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, 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^=1; swap(lt, rt);}
inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<15|rand(); tree[x].sum=tree[x].mn=delta; tree[x].size=1;}
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+1;
}
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=0;
}
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=0, r=x;
    else if(k==tree[x].size) l=x, r=0;
    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-1), 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-1);
    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-1);
    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-1);
    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-1); 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-1);
    ans=tree[x].mn;
    merge(x, z, x); merge(root, x, y);
    return ans;
}
int main()
{
    srand(19260817); read(n); tree[0].rnd=tree[0].mn=tree[0].sum=inf;
    for(int i=1;i<=n;i++) read(x), build(tmp, x), merge(root, root, tmp);
    read(m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s", s+1);
        if(s[1]==‘A‘) read(x), read(y), read(z), add(x, y, z);
        else if(s[1]==‘R‘ && s[4]==‘O‘) read(x), read(y), read(z), revolve(x, y, z%(y-x+1));
        else if(s[1]==‘R‘ && s[4]==‘E‘) read(x), read(y), reverse(x, y);
        else if(s[1]==‘D‘) read(x), del(x);
        else if(s[1]==‘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=50010, inf=1e9+1;
int n, m, ty, x, y, z, root, tott;
struct treap{int rnd, mx, sum, delta, rev, size, ls, rs;} tree[maxn];
char buf[20000010],*ptr=buf-1;
inline int read()
{
    char c=*++ptr; int s=0,t=1;
    while(c<48||c>57) t=-1, c=*++ptr;
    while(c>=48&&c<=57) s=s*10+c-‘0‘, 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^=1; 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+1;
}
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=0;
}
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=0, r=x;
    else if(k==tree[x].size) l=x, r=0;
    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-1), 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-1);
    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-1);
    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-1);
    ans=tree[x].mx;
    merge(x, z, x); merge(root, x, y);
    return ans;
}
int main()
{
    fread(buf,1,sizeof(buf),stdin); n=read(); m=read(); tree[0].rnd=inf; tree[0].mx=tree[0].sum=-inf;
    for(int i=1;i<=n;i++) tree[++tott].size=1, tree[tott].rnd=rand()<<15|rand(), merge(root, root, tott);
    for(int i=1;i<=m;i++)
    {
        ty=read(); x=read(); y=read();
        if(ty==1) z=read(), add(x, y, z);
        else if(ty==2) 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=500010, inf=1e9+1;
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=1; k=0; char c=getchar();
    while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar();
    while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar();
    k*=f;
}
inline void reverseone(int x) {tree[x].rev^=1; swap(lt, rt);}
inline void build(int &x, int delta) {tree[x=++tott].rnd=rand()<<15|rand(); tree[x].sum=delta; tree[x].size=1;}
inline void up(int x) {if(!x) return; tree[x].size=tree[lt].size+tree[rt].size+1;}
inline void down(int x)
{
    if(!x) return;
    if(tree[x].rev) reverseone(lt), reverseone(rt);
    tree[x].rev=0;
}
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=0, r=x;
    else if(k==tree[x].size) l=x, r=0;
    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-1), up(x);
}
inline void reverse(int l, int r)
{
    int x, y, z;
    split(root, x, y, r); split(x, z, x, l-1);
    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(19260817); read(n); read(m); tree[0].rnd=tree[0].sum=inf;
    for(int i=1;i<=n;i++) build(tmp, i), merge(root, root, tmp);
    for(int i=1;i<=m;i++) read(x), read(y), reverse(x, y);
    print(root);
}
                    

  例5 bzoj 1500

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

时间: 2024-10-19 02:17:53

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

莫队算法&amp;#183;初探总结

莫队算法分那么几类: 普通序列 带修改 树上 回滚 支持在线 其实上述的类型还可以组合起来(非常的毒瘤). 个人理解莫队算法的精髓在于如何利用暴力将答案再合理的时间和空间内跑出来.说白了: \[莫队算法=一种很牛逼的自定义排序+分块处理+暴力 \] 首先要理解自定义排序,这个排序之后整个序列可以最快地处理所有的询问(这里暂时不谈第五类问题(支持在线),这里认为莫队是只能离线处理问题的,必须先把所有的问题都离线下来).怎么为之快,快要看左端点移动的总距离+右端点移动的总距离最小.那么一般用块的奇偶

强连通分量算法&#183;$tarjan$初探

嗯,今天好不容易把鸽了好久的缩点给弄完了--感觉好像--很简单? 算法的目的,其实就是在有向图上,把一个强连通分量缩成一个点--然后我们再对此搞搞事情,\(over\) 哦对,时间复杂度很显然是\(\Theta(n)\)的,懒得\(Proof\)了. 真是简明扼要的算法啊\(233\) 比较弱智的代码是下面的: #include <stack> #include <cstdio> #include <iostream> #define min Min #define m

初探12306售票算法(二)-java代码实践

周五闲来无事,基于上一篇关于初探12306售票算法(一)-理论,进行了java编码实践供各位读者参考(以下为相关代码的简单描述) 1.订票工具类 1.1初始化一列车厢的票据信息 /** * 生成Ticket信息 * * @param train * @return */ public static List<Ticket> initTicketList(Train train) { List<Ticket> result = new ArrayList<Ticket>(

初探STL之算法

算法 STL算法部分主要由头文件<algorithm>,<numeric>,<functional>组成.要使用 STL中的算法函数必须包含头文件<algorithm>,对于数值算法须包含<numeric>,<functional>中则定义了一些模板类,用来声明函数对象. 分类 STL中算法大致分为四类: 1.非可变序列算法:指不直接修改其所操作的容器内容的算法. 2.可变序列算法:指可以修改它们所操作的容器内容的算法. 3.排序算法

台大林轩田机器学习课程笔记----机器学习初探及PLA算法

机器学习初探 1.什么是机器学习 学习指的是一个人在观察事物的过程中所提炼出的技能,相比于学习,机器学习指的就是让计算机在一堆数据中通过观察获得某些经验(即数学模型),从而提升某些方面(例如推荐系统的精度)的性能(可测量的). 2.机器学习使用的条件 需要有规则可以学习 有事先准备好的数据 编程很难做到 3.机器学习所组成的元素 输入X 输出 Y 目标函数f:X->Y 数据(训练集):D={(x1,y1),(x2,y2),-.(xn,yn)}假设(skill):g:X->Y 4.如何使用机器学

数据挖掘领域十大经典算法初探

译者:July   二零一一年一月十五日 ----------------------------------------- 参考文献:国际权威的学术组织ICDM,于06年12月年评选出的数据挖掘领域的十大经典算法:C4.5, k-Means, SVM, Apriori, EM, PageRank, AdaBoost, kNN, Naive Bayes, and CART.==============博主说明:1.原文献非最新文章,只是本人向来对算法比较敏感.感兴趣,便把原文细看了下,翻译过程中

初探几种排序算法

多种排序算法的总结(不包括复杂度的详细推算) 稳定排序与不稳定排序 稳定排序:相同元素在排序中的相对位置不改变. 不稳定排序:相同元素在排序中的相对位置改变. 内部排序与外部排序: 内部排序:待排的记录与内容都放在计算机的随机存储器中进行的排序过程 外部排序:一般指待排序记录的数量很大,以致内存中一次不能完全容纳全部的记录,在排序过程中,需要对外存进行访问的排序过程. 排序算法的复杂度: 在此我们一般只要考虑的就是时间复杂度,当然个别排序的空间复杂度也很大,如希尔排序.这里时间复杂度的计算方法和

Vue源码终笔-VNode更新与diff算法初探

写完这个就差不多了,准备干新项目了. 确实挺不擅长写东西,感觉都是罗列代码写点注释的感觉,这篇就简单阐述一下数据变动时DOM是如何更新的,主要讲解下其中的diff算法. 先来个正常的html模板: <body> <div id='app'> <div v-for="item in items">{{item}}</div> <div @click='click'>click me!</div> </div&g

算法-回溯法初探-n皇后问题

问题描述: 这周的数据结构作业要求写一个程序判断输入为n的所有皇后的情况, 皇后大致就是在一个n*n的棋盘上所有不同行列及不同对角线的格子排列 提示用书本上求解迷宫时用到的回溯法,也就是用到一个栈来保存当前满足的皇后,若进行不下去则回溯 采用C语言实现 代码: 1,文件 BetterQueen.h 里面主要定义了一些程序要用到的数据结构和函数接口 #ifndef BETTERQUEEN_H_INCLUDED #define BETTERQUEEN_H_INCLUDED #include <std