zoukankan      html  css  js  c++  java
  • [模板][数据结构] 树状数组

    树状数组及各种操作

    同样是先挖坑再慢慢填

    首先要知道一个贯穿树状数组始终的操作:lowbit

    int lowbit(int x){
        return x & (-x);
    }
    

    这个函数的意思是:返回 (x) 的二进制表达中第一个 (1) 的位置(以 (2^i) 的形式表示)

    如何做到的:因为整数是以补码表示的,

    举个例子:

    ((2)_{dec} = (00000010)_{bin})

    $ (-2)_{dec} = (11111110)_{bin} $
    

    ​ 所以 lowbit(2) = 2

    而这个 lowbit 值恰好表示了树状数组上一个点所包含的区间

    以树状数组为c[],原数组为a[]为例:

    c[1] = a[1]

    c[2] = a[1] + a[2]

    c[3] = a[3]

    c[4] = a[1] + a[2] + a[3] + a[4]

    以此类推。

    这样我们就有了树状数组的基本结构。


    单点修改 + 区间求和

    还是举例子:

    ​ 操作:将 a[1] 加上 (5)

    ​ 那么管理它的每一个位置都要加上 (5)

    ​ 即 c[1]c[2]c[4]……… 都要加上 (5)

    这时我们会发现每个要更新的数组恰恰为前一个数组的位置加上lowbit值。

    于是有以下代码:

    void add(int val, int pos){
        while(pos <= n){
            c[pos] += val;
            pos += lowbit(pos);
        }
    } // 单点加
    

    至于单点修改为某值仅需要略微修改,不再赘述。

    而区间求和自然就不难了,只需逆向进行上面的操作(然后作个差):

    int getsum(int pos){
        int ans = 0;
        while(pos > 0){
            ans += c[pos];
            pos -= lowbit(pos);
        }
        return ans;
    } // 求 1 ~ pos 之和 
    

    区间修改 + 单点求和

    (这个其实不常用,一般用区间求和版本的比较多)

    将树状数组维护的数组建为差分数组,这样修改区间值的时候实际只需要在差分数组的 (l)(r+1) 位置操作。

    注意此时的 getsum 返回的是询问的 pos 的值。

    void rangeAdd(int l, int r, int val){
        add(l, val); add(r+1, -val);
        return;
    }
    
    LL getsum(int pos){
        LL ans = 0;
        while(pos > 0){
            ans += d[pos];
            pos -= lowbit(pos);
        }
        return ans;
    }
    

    此时的 add 函数没有变化。

  • 相关阅读:
    leetcode931
    leetcode1289
    leetcode1286
    poj Meteor Shower
    noip杂题题解
    noip2007部分题
    NOIP Mayan游戏
    某模拟题题解
    codevs 1423 骑士
    noip 邮票面值设计
  • 原文地址:https://www.cnblogs.com/Foggy-Forest/p/13023123.html
Copyright © 2011-2022 走看看