重学树状数组6/14(P3368 【模板】树状数组 2)

前几天考了个树状数组的题.....不会 别人都A了 十分丢人

于是回来重学一遍 幸亏60min学会了(我好菜啊)


首先树状数组是一种树形结构,与线段树不同的是它只能查前缀和,只支持单点修改。(以后可能还可以扩展吧)

画出一棵树状数组来之后我们可以看到 对于每一个tr点 存的都是一个区间的和 这个区间范围被特殊的位运算规则存在了tr下标里

单点修改的方式是

void add(int x,int k) {//a[i] + k
    while (x<=n){
        tr[x] += k;
        x += lowbit(x);
    }
}

我们既然对一个点进行了修改,那么在tr数组中包含了这个点的tr也要进行修改

修改的方式就是在原下标上不断地加他当前的lowbit作为新下标,然后对此tr做加法

如果换成位运算的方式,就是在每一位1上加(这个说法太xie了 我不会说= =)(所以这个范围规则真的很鬼)

求1~x前缀和的方式是

int query(int x){// 1 ~x sum
    int ret = 0;
    while(x>0){
        ret += tr[x];
        x -= lowbit(x);
    }
    return ret;
}

这也是不断地在当前数上减去他的lowbit然后求和(感性理解(这个真能感性理解但给定义很难))

对于普通数组来说,求范围和是O(n)的,而树状数组是O(logn)的

单点修改普通数组O(1),树状数组O(logN)(其中N为数组总大小)

区间修改普通数组加查分O(1)树状数组O(logN)(其中N为数组总大小)

最后给出luogu树状数组2的代码

#include<bits/stdc++.h>

using namespace std ;

const int MAXN = 500010;
int tr[MAXN],n,m,a[MAXN],x,y,z;
int lowbit(int x){return x & (-x);}
void add(int x,int k) {//a[i] + k
    while (x<=n){
        tr[x] += k;
        x += lowbit(x);
    }
}
int query(int x){//1~x sum
    int ret = 0;
    while(x>0){
        ret += tr[x];
        x -= lowbit(x);
    }
    return ret;
} 

signed main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    int last=-1,now=-1;
    cin>>last;
    a[1] = last;
    for(int i=2;i<=n;i++){
        cin>>now;
        a[i] = now - last;
        last = now ;
    }
    for(int i=1;i<=n;i++){
        add(i,a[i]);
    }
//    for(int i=1;i<=n;i++){
//        cout<<"a["<<i<<"]="<<a[i]<<endl;
//    }
    for(int i=1;i<=m;i++){
        int flag;
        cin>>flag;
        if(flag == 1){
            cin>>x>>y>>z;
            add(x,z);
            add(y+1,-z);
        }
        if(flag == 2){
            cin>>x;
            cout<<query(x)<<endl;
        }
    }
    return 0;
}

 

像这种基础数据结构就要常常回来复习,万一哪天考了呢

TAG:SIN_XIII 

posted @ 2019-06-14 12:41  SIN_XIII  阅读(118)  评论(0编辑  收藏  举报