ǰ֪ʶ
lowbit=x&(-x)
lowbit表示的是转换成二进制取第一个一和后面的0得到的数,树状数组经常用到lowbit(原理我也不知道)
树状数组只支持单点修改和前缀查询,其他可以通过拓展
时间复杂度分析:
单点修改O(log n)
前缀和查询O(log n)
树状数组操作:
1.lowbit();
2.modify(x,d);//用于将点x加上数d
3.query(int x);//表示算出到x的前缀和
经典例题
1.洛谷P3374
裸的树状数组,直接按照需要的操作进行单点修改和前缀和查询,在利用前缀和的性质,来算出区间答案
cpp代码如下(代码中,a[i]表示一开始第i项的初始值,s[i]表示编号为i的数在维护的树状数组里,每个编号的数
#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <cstdio> #include <vector> #include <cstring> #include <cstdlib> #include <iostream> #include<algorithm> #include<cmath> #include<stdlib.h> using namespace std; int a[500005],n,m,s[500005]; int lowbit(int x) { return x&(-x); } void modify(int x,int d) { for(int i=x;i<=n;i+=lowbit(i)) { s[i]+=d; } } int qusry(int x)//返回前x项的前缀和 { int sum=0; for(int i=x;i;i-=lowbit(i)) { sum+=s[i]; } return sum; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); modify(i,a[i]); } for(int i=1;i<=m;i++) { int xx,x,y,k; cin>>xx; if(xx==1) { scanf("%d%d",&x,&k); modify(x,k); } if(xx==2) { scanf("%d%d",&x,&y); cout<<qusry(y)-qusry(x-1)<< endl; } } return 0; }
其实就是由单点修改,变成了区间修改,根据差分数组,只需要由单点修改改成双点修改就可以了
cpp代码如下(带注释)
#include <map> #include <set> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <cstdio> #include <vector> #include <cstring> #include <cstdlib> #include <iostream> #include<algorithm> #include<cmath> #include<stdlib.h> using namespace std; int n,m,a[500001],s[500001],c[500001];//a数组表示第i项的初始值 int lowbit(int x) { return x&(-x); } void modify(int x,int d) { for(int i=x;i<=n;i+=lowbit(i)) { c[i]+=d; } } int qusry(int x) { int sum=0; for(int i=x;i;i-=lowbit(i)) { sum=sum+c[i]; } return sum; } //以上皆为树状数组正常操作 int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]);//读入初始值 } for(int i=1;i<=n;i++) { s[i]=a[i]-a[i-1];//维护一个差分数组 modify(i,s[i]);//将差分数组形成一个新的树状数组 } for(int i=1;i<=m;i++) { int xx,x,y,k; scanf("%d",&xx); if(xx==1) { scanf("%d%d%d",&x,&y,&k); modify(x,k); modify(y+1,-k); //根据差分数组,只需要将他的头指针+k,(尾指针+1)的指针的值-k,就可以将x~y区间的值都加上k } if(xx==2) { scanf("%d",&x); cout<<qusry(x)<< endl;//查询的这个数就为他的差分数组的前缀和 } } return 0; }