可并堆 学习 (兼模板)

先存板子:

int root[maxn]; // 这个点维护的堆实际编号.
struct Ltree {
    int ls[maxn<<1], rs[maxn<<1]; // 左右儿子的编号
    ll val[maxn<<1], mul[maxn<<1], add[maxn<<1];
    // 该点的值, 改点的+/*的标记, 因为左偏树也是二叉树
    // 所以可以想线段树那样进行下放
    void funmul(int id, ll tmp) {
        val[id] *= tmp; mul[id] *= tmp;
        add[id] *= tmp;
    }
    void funadd(int id, ll tmp) {
        val[id] += tmp; add[id] += tmp;
    }
    void pushdown(int id) {
        if (mul[id] != 1) {
            funmul(ls[id], mul[id]);
            funmul(rs[id], mul[id]);
            mul[id] = 1;
        }
        if (add[id]) {
            funadd(ls[id], add[id]);
            funadd(rs[id], add[id]);
            add[id] = 0;
        }
    }
    int Un(int x, int y){  // 小根堆/ 看情况维护, 改个符号就行
        if (!x || !y) return x+y;
        if (val[x] > val[y]) swap(x,y);
        pushdown(x);
        rs[x] = Un(rs[x], y);
        swap(ls[x], rs[x]);
        return x;
    }
    void pop(int &x) {
        x = Un(ls[x], rs[x]);
    }
}heap;
// 取top直接取对应的val值即可

讲讲为啥用左偏树. 用这个主要是维护一个堆, 一般的堆如果合并的话那么暴力的插入一定为T, 所以我们要堆二叉树的结果换一种, 实际上就是用左偏树即可, 顾名思义, 这颗二叉树是往左边偏的. 优势在于合并两个堆的复杂是log级别的, 基本上适用…大概知道优势即可, 然后学会了就套套板子即可…

总结题目

这一类题目大多数都是有很明显的在不同集合的最值之间进行操作, 然后还有就是有合并的特性, 此时就肯定要用可并堆, 需要注意的几点就是是否涉及并查集… 如果是的话注意将删去的那个点的根节点重新设置为新的根节点, 具体请参照BZOJ-1455

注意: 不一定是把根节点初始化为自身, 除非是每一个堆和自身都有关系, 大部分题是这样, 但是有些题目不一定, 如果不是自身相关的, 就初始化为零, 然后把相应点连进去即可, 0的就可以不用管.

相关题目可看这一分类的其他文章.

你可能感兴趣的:(可并堆/左偏树)