平衡树之文艺平衡树(维护区间)

2022年01月16日,第三天

平衡树——文艺平衡树(维护区间)

首先我们需要知道如何用 f h q   T r e a p fhq\ Treap fhq Treap 来实现区间操作

例如,要操作的区间为 [ l ,   r ] [l,\ r] [l, r] ,那么我们就在 f h q   T r e a p fhq\ Treap fhq Treap 里把这一段直接 s p l i t split split 拆出来进行操作然后再合并回去。按大小分裂:把树拆成两颗树,其中一颗树的大小等于给定的大小,剩余部分在另一颗数里。

具体步骤:把 f h q   T r e a p fhq\ Treap fhq Treap 按大小 l − 1 l - 1 l1 拆分成 x x x y y y ,再把 y y y 按大小 r − l + 1 r-l + 1 rl+1 拆分成 y y y z z z ,此时 y y y 树就是我们要操作区间的平衡树,最后要把 x , y , z x,y,z xyz 合并回去。

除了这些,我们需要使用线段树中常见的概念:懒标记 ( l a z y   t a g ) (lazy\ tag) (lazy tag) 。每当我们想要对一个区间进行翻转时,我们就对这个区间维护懒标记,如果没有懒标记,那就打上,如果有懒标记,那就去掉,因为翻转两次等于没有翻转,别忘了写懒标记的下传操作,如果当前结点的左右子树在后续代码中有被更改的风险的话,那就下传,以防万一。

struct node {
    int l, r;
    int val, key;
    int size;
    bool lazy;   // 新加一个懒标记表示当前区间要不要翻转
}t[N];
mt19937 rnd(233);
int cnt, root;
inline int newnode (int val) {
    t[++ cnt].val = val;
    t[cnt].key = rnd();
    t[cnt].size = 1;
    t[cnt].l = t[cnt].r = t[cnt].lazy = 0;
    return cnt;
}

inline void pushup (int now) {
    t[now].size = t[t[now].l].size + t[t[now].r].size + 1;
}

inline void pushdown (int now) {    // 下传懒标记
    swap (t[now].l, t[now].r);
    t[t[now].l].lazy ^= 1;
    t[t[now].r].lazy ^= 1;
    t[now].lazy = false;
}

void split (int now, int siz, int& x, int& y) {  // 按大小分裂
    if (! now) x = y = 0;
    else {
        if (t[now].lazy) pushdown(now);
        if (t[t[now].l].size < siz) {
            x = now;
            split (t[now].r, siz - t[t[now].l].size - 1, t[now].r, y);
        }else {
            y = now;
            split (t[now].l, siz, x, t[now].l);
        }
        pushup(now);
    }
}

int merge (int x, int y) {    // 合并
    if (! x || ! y) return x | y;
    if (t[x].key < t[y].key) {
        if (t[x].lazy) pushdown (x);   // 注意要下传懒标记
        t[x].r = merge (t[x].r, y);
        pushup (x);
        return x;
    }else {
        if (t[y].lazy) pushdown (y);
        t[y].l = merge (x, t[y].l);
        pushup (y);
        return y;
    }
}

void reverse (int l, int r) {    // 翻转
    int x, y, z;
    split (root, l - 1, x, y);
    split (y, r - l + 1, y, z);
    t[y].lazy ^= 1;
    root = merge (merge (x, y), z);
}

void inorder (int now) {
    if (! now) return ;
    if (t[now].lazy) pushdown (now);
    inorder (t[now].l);
    cout << t[now].val << ' ';
    inorder (t[now].r);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值