题目链接: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 ",query(y)-query(x-1)); } return 0; }