树状数组

相比于线段树,节省了很多的内存
特殊用处:求逆序对以及在CDQ分治里嵌套用

一维树状数组

img

黑色部分代表原来的数组,用\(A[i]\)表示,红色部分代表树状数组,用\(C[i]\)表示。

\(C[1] = A[1]\\C[2] = A[1] + A[2]\\C[3]=A[3]\\C[4]=A[1]+A[2]+A[3]+A[4]\\C[5]=A[5]\\C[6] = A[5]+A[6]\\C[7] = A[7]\\C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]\)

发现有规律,\(C[i] = A[i - 2^k + 1] + A[i - 2^k+2]+...A[i]\)k为二进制中最低位到最高位连续0的长度

那么对于求和,求前7项和,\(sum = C[7]+C[6]+C[4]\)

可以得到\(SUM[i] = C[i] + C[i - 2^{k_1}]+C[(i-2^{k_1}) -2^{k_2}]+...\)

lowbit

定义\(lowbit(x)\)是x的二进制表示形式中最低位的1和后面的0组成的数

二进制的负数是正数取反加1。如果x = 12(1100),那么-x = -12(0100)。lowbit(x)=4

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

更新

对于单点修改,如果要修改\(A[3]\)的值,那么对于\(C[3],C[4],C[8]\)都要进行修改

是一个向右上跳的过程

img

void update(int i, int k){
    for(; i <= n; i += lowbit(i))c[i] += k;
}

查询

\([1,i]\)的和。比如查询[1,5]的和,就是\(C[4]+C[5]\)

是一个向左上角跳的过程

int query(int i){
    int ans = 0;
    for(; i > 0; i -= lowbit(i))ans += c[i];
    return ans;
}

因为求出来的是前缀和形式,所以对于求[L,R],可以用query(R) - query(L-1);

模板

单点修改 + 区间查询

传送门

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e5 + 5;
struct BIT{
    int c[N];
    BIT(){memset(c, 0, sizeof(c));}
    int lowbit(int x){
        return x & (-x);
    }
    void update(int x, int k){// 修改a[x] 的值
        for(; x <= N; x += lowbit(x))
            c[x] += k;
    }
    int query(int x){//查询[1, x]的值
        int ans = 0;
        for(; x > 0; x -= lowbit(x)) 
            ans += c[x];
        return ans;
    }
};
int main(){
    BIT b;
    return 0;
}

区间修改,单点查询

传送门

利用前缀和和差分思想,初始的树状数组是一个差分数组 对于每一个区间[l,r]修改,变为b[l]++, b[r + 1]-- 对于单点查询,就是[1,x]的前缀和

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int N = 5e5 + 5;
int n, m;
struct BIT{
    ll c[N];
    BIT(){memset(c, 0, sizeof(c));}
    ll lowbit(ll x){
        return x & (-x);
    }
    void Add(int x, ll k){
        for(; x <= N; x += lowbit(x))c[x] += k;
    }
    ll Ask(ll x){//[1, x]和
        ll ans = 0;
        for(;x; x -= lowbit(x))ans += c[x];
        return ans;
    }
};
int main(){
    BIT bit;
    ll last = 0, now;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++){
        scanf("%lld", &now);
        bit.Add(i, now - last);//差分
        last = now;
    }
    for(int i = 1; i <= m; i++){
        ll op, l, r, c;
        scanf("%lld", &op);
        if(op == 1){
            scanf("%lld%lld%lld", &l, &r, &c);
            bit.Add(l, c); bit.Add(r + 1, -c);//差分
        }else {
            scanf("%lld", &l);
            printf("%lld\n", bit.Ask(l));
        }
    }
    return 0;
}

二维树状数组

struct BIT{
    int c[N][N];
    BIT(){memset(c, 0, sizeof(c));}
    int lowbit(int x){
        return x & (-x);
    }
    void add(int x, int y, int k){//点a[x][y] + k
        while(x <= N){
            while(y <= N){
                c[x][y] += k;
                y += lowbit(y);
                x += lowbit(x);
            }
        }
    }
    int sum(int x, int y){//求[1][1] 到[x][y] 的和
        int ans = 0;
        while(x){
            while(y){
                ans += c[x][y];
                x -= lowbit(x);
                y -= lowbit(y);
            }
        }
        return ans;
    }      
};
posted @ 2019-09-18 20:04  Emcikem  阅读(133)  评论(0编辑  收藏  举报