平衡树

学太多平衡树也没有用

所以这里侧重点不一样

来介绍一些平衡树

FHQ Treap

无旋 \(Treap\)

通过合并与分裂来维护堆的性质, 代码都比较容易理解

  • 一些基本操作, 可以开个栈来实现空间回收
struct Tree {
    int l, r;
    int dat, val;
    int cnt, size;
} a[N];
int n, tot, root;

int New(int val) {
    a[++tot].val = val;
    a[tot].dat = std::rand();
    a[tot].size = 1;
    return tot;
}

void updata(int pos) {
    a[pos].size = a[a[pos].l].size + a[a[pos].r].size + 1;
}
  • 分裂
void split(int p, int &x, int &y, int k) {
    if (p == 0) {
        x = y = 0;
        return ;
    }

    if (a[p].val <= k) {
        x = p;
        split(a[p].r, a[p].r, y, k);
    } else {
        y = p;
        split(a[p].l, x, a[p].l, k);
    }

    updata(p);
}
  • 合并
int merge(int A, int B) {
    if (!A || !B)
        return A + B;

    if (a[A].dat < a[B].dat) {
        a[A].r = merge(a[A].r, B);
        updata(A);
        return A;
    } else {
        a[B].l = merge(A, a[B].l);
        updata(B);
        return B;
    }
}
  • \(k\)
int kth(int p, int k) {
    while (true) {
        if (k <= a[a[p].l].size)
            p = a[p].l;
        else if (k == a[a[p].l].size + 1)
            return p;
        else
            k -= a[a[p].l].size + 1, p = a[p].r;
    }
}
  • 插入与删除
void insert (int &root, int k) {
    split(root, x, y, k);
    root = merge(merge(x, New(k)), y);
}
void remove (int &root, int k) {
    split(root, x, z, k);
    split(x, x, y, k - 1);
    if (y) sta.push(y);
    y = merge(a[y].l, a[y].r);
    root = merge(merge(x, y), z);
}

查询前驱后继可以通过 分裂与 \(kth\) 实现

【模板】普通平衡树

一些重要的东西来了

  • 无交合并

看代码也可以比较容易的理解

int merge (int A, int B) {
	int C = 0;
	while (A && B) {
		if (a[A].Min > a[B].Min) std::swap(A, B);
		int left = 0;
		split (A, left, A, a[B].Min);
		C = Merge (C, left);
	}
	C = Merge (C, A + B);
	return C;
}

算是一个科技了, 复杂度是 \(log^2\)

不会分析, 可以用来做函数复合问题

  • 区间操作

平衡树分裂的时候按照 \(kth\) 的方法来分裂

合并的时候按顺序合并

\([1, l - 1], [l, r], [r + 1, n]\)

分成这三个区间, 中间整体打标记, 然后就可以做了

容易理解且好写

【模板】文艺平衡树

  • 可持久化

可持久化需要把每个历史版本都记录下来, 但是我们发现修改的地方很少, 所以有一些东西是重复的, 可以直接继承, FHQ_Treap 的简单操作, 可以让他很愉快的进行可持久化

只需在合并与分裂的时候, 不去继承原先的节点, 而是去新开一个节点就可以了

空间可能需要的多一点

【模板】可持久化平衡树

【模板】可持久化文艺平衡树

注意翻转 \(push\) 的时候需要再次新建节点

因为对他进行了修改

posted @ 2025-02-16 19:20  d3genera7e  阅读(17)  评论(0)    收藏  举报