树状数组模板1

题目链接:https://www.luogu.org/problemnew/show/P3374

  树状数组和线段树一样,也是一个结点表示一个段,只不过线段树是采用二分思想来表示一个线段,而树状数组不是。对于原数据a[],树状数组tr[],tr[n]=tr[n-2^k+1]+...+tr[n],其中k是n在二进制下末尾0的个数,2^k可通过x&(-x)来获得。树状数组的适用性约束较大,一般用于对点更新(O(logn)),对区间查询(O(logn)),而不建议用来对区间更新(O(nlogn)),还不如在原数组上操作(O(n)),不建议对点查询。而且必须满足减法规则才能使用树状数组,即比如求和就满足sum(a,b)=sum(1,b)-sum(1,a-1),而求最大值就不满足减法规则。

  在很多情况下线段树都可以用树状数组实现,凡是能用树状数组实现的都可以用线段树实现。树状数组相比线段树来说更节省空间且复杂读更低,但局限性稍大。

  树状数组和线段树都在题目要求进行频繁的修改和查询操作时使用。线段树的使用更灵活,主要是考虑每个结点需要记录什么,这个记录的值在修改过程需要怎么维护。线段树之所以存在的理由是因为它能适用于很多方面,不仅仅是区间、单点的查询修改,还有标记等等,可以用于模拟、DP等等,而且空间经过离散化以后也可以相对压缩,所以适用范围线段树更加广一些。

AC代码:

#include<cstdio>
#include<cctype>
using namespace std;

inline int read(){
    int x=0,f=0;char c=0;
    while(!isdigit(c)){f|=c=='-';c=getchar();}
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return f?-x:x;
}

int n,m,tr[500005];

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

void update(int pos,int num){
    while(pos<=n){
        tr[pos]+=num;
        pos+=lowbit(pos);
    }
}

int query(int pos){
    int ans=0;
    while(pos>0){
        ans+=tr[pos];
        pos-=lowbit(pos);
    }
    return ans;
}

int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i){
        int tmp=read();
        update(i,tmp);
    }
    while(m--){
        int op=read(),x=read(),y=read();
        if(op==1)
            update(x,y);
        else
            printf("%d\n",query(y)-query(x-1));
    }
    return 0;
}

 

posted @ 2019-04-30 16:50  Frank__Chen  阅读(128)  评论(0编辑  收藏  举报