可持久化平衡树: 无旋Treap

版权声明:未经作者本人允许禁止转载。 https://blog.csdn.net/jokerwyt/article/details/81710270

例题

jzoj3658文本编辑器

无旋Treap

有旋Treap由于splay的存在而无用武之地了。
优点:代码较短,原生支持区间分裂合并,并支持可持久化
缺点:较splay大概有2的常数。

核心操作

定义请戳

split(root,x)
将树root分解为两颗树,第一颗为其中[1,x],第二颗为[x+1,size]
返回是保存上述两个根的pair。

merge(a,b)
将a,b合为一颗树,返回这棵树的根。

*何时维护堆的性质:merge合并时,判断a,b中哪个点应该为另一个点的父亲。

点的期望树高是 log n
区间修改就与splay等一样打lazy标记。

口胡时间证明

考虑treap的构造方法(选一个权值最大的作为根),其实树的形态就是笛卡尔树。随机序列唯一对应着一颗笛卡尔树,随机树的期望高度是 log n
证明:
f i 为有i个点的期望深度和。显然

f i + 1 = f i + ( f i / i ) + 1
,展开化简后 f n = O ( n log n ) ,因此每个点 期望树高是log的。
(我为什么要证这个常识???)

可持久化

可持久化后:
split(root,x),返回两颗新树(保有一些连回原树的边,原树不作改动)
merge(a,b),返回一颗新树(同上)

给一个点打区间标记时:需要开新点,不能改原点。这意味着down也可能要开新点。

注意一个key的问题:
size[a]>size[b] (假的)
key[a]< key[b] (不可持久化时用)
rand() % (size[a] + size[b]) < size[a] (可持久化时用)

2无法在可持久化时使用是因为可能碰到自己与自己合并的情况。这样无法保证树形态随机了。因为按照大小赋权随机,大上小下即可。
据说: 不可持久化的情况下2,3是基本一致的。

可供参考的实现(自我感觉真良好)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#define update(x) (size[x]=size[c[x][0]]+size[c[x][1]]+1)
using namespace std;
const int N = 1e5+10,C = 190 * N;
typedef pair<int,int> D;

int tot,fa[C],c[C][2],size[C],lazy[C],root;
char val[C];
int m;

int newnode(int x = 0) {
    ++tot;
    fa[tot] = fa[x];
    size[tot] = size[x];
    lazy[tot] = lazy[x];
    memcpy(c[tot],c[x],sizeof c[x]);
    val[tot] = val[x];
    return tot;
}

int rever(int x) {
    if (x) {
        int w = newnode(x);
        lazy[w]^=1;
        swap(c[w][0],c[w][1]);
        return w;
    } else return 0;
}

void down(int x) {
    if (x && lazy[x]) {
        c[x][0] = rever(c[x][0]);
        c[x][1] = rever(c[x][1]);
        lazy[x] = 0;
    }
}

D split(int x,int k) {
    if (!x) return (D){0,0};
    down(x); D y;
    int z = newnode(x);
    if (size[c[x][0]] >= k) {
        y = split(c[x][0],k);
        c[z][0] = y.second; update(z);
        y.second = z;
    } else {
        y = split(c[x][1],k-size[c[x][0]]-1);
        c[z][1] = y.first; update(z);
        y.first = z;
    }
    return y;
}

int merge(int a,int b) {
    if (!a || !b) return a+b;
    down(a),down(b); int q = 0;
    if (rand() % (size[a] + size[b]) <= size[a] + 15/*magic*/) {
        q = newnode(a);
        c[q][1] = merge(c[q][1],b);
    } else {
        q = newnode(b);
        c[q][0] = merge(a,c[q][0]);
    }
    update(q);
    return q;
}
char zc;
void read(int &x) {
    while ((zc=getchar()) < '0' || zc > '9');
    x = zc - '0';
    while ((zc=getchar())>='0' && zc <='9') x = x * 10 + zc - '0';
}
int main() {
    freopen("editor.in","r",stdin);
    freopen("editor.out","w",stdout);
    cin>>m; 
    for (int i = 1; i <= m; i++) {
//      cout<<endl;
//      out(root); cout<<"::"<<endl;
        scanf("\n");
        char op; op = getchar();
        if (op == 'I') {
            int x; char ch; read(x); ch = getchar();
            D y = split(root, x);
            int zz = newnode(); val[zz] = ch; size[zz] = 1;
            root = merge(y.first, merge(zz, y.second));
        } else 
        if (op == 'D') {
            int l,r; read(l), read(r);
            D y = split(root, l - 1);
            D z = split(y.second, r - l + 1);
            root = merge(y.first, z.second);
        } else 
        if (op == 'C') {
            int l,r,w; read(l),read(r),read(w);
            D y = split(root, l - 1);
            D z = split(y.second, r - l + 1);
            int k = z.first;
            z = split(root, w);
            root = merge(z.first, merge(k, z.second));
        } else
        if (op == 'R') {
            int l,r; read(l),read(r);
            D y = split(root, l - 1);
            D z = split(y.second, r - l + 1);
            root = merge(y.first, merge(rever(z.first), z.second));
        } else {
            int w; read(w);
            D y = split(root, w - 1);
            y = split(y.second, 1);
            putchar(val[y.first]);
//          root = merge(y.first, merge(z.first, z.second));
        }
    }
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/81710270