树状数组是利用了数的二进制特征进行检索的一种树状结构。关于树状数组的结构图可以看下这篇文章,这位博主的结构图很详细(树状数组结构图)那么树状数组我们是用来干什么的呢?其实它和前缀和与差分也是也是一样的,也是用来求解关于区间问题的,具体我们可以看看后面的代码展现来感受一下就知道了。
我们定义一个树状数组tree[],原始数组a[]
由图的结构我们可以知道tree[1]=a1
tree[2]=tree[1]+a2
tree[3]=a3
tree[4]=tree[2]+tree[3]+a4
.....
tree[8]=tree[4]+tree[6]+tree[7]+a8
我们会发现一个问题,该如何求出tree呢?这就需要在每次的二进制后面加上一,找到下一个数了。这就需要用到lowbit(x)=x&(-x)来找到二进制中的最后一个一了。
如:
- x x二进制 lowbit(x) tree
- 1 1 1 tree[1]=a1
- 2 10 2 tree[2]=a1+a2
- 3 11 1 tree[3]=a3
- 4 100 4 tree[4]=a4+a3+a2+a1
- 5 101 1 tree[5]=a5
- 6 110 2 tree[6]=a6+a5
相信规律很明显我就不细说了吧。
- #include
- using namespace std;
- #define lowbit(x)((x)&-(x))
- const int N=1e3+10;
- int a[N];
- int tree[N];
- int n;
-
- void up(int x,int d)
- {
- while(x<=N){
- tree[x]+=d;
- x+=lowbit(x);
- }
- }
-
- int sum(int x)
- {
- int ans=0;
- while(x>0){
- ans+=tree[x];
- x-=lowbit(x);
- }
- return ans;
- }
-
- int main()
- {
- cin>>n;
- for(int i=1;i<=n;i++){
- cin>>a[i];
- up(i,a[i]);
- }
- cout<<sum(6)-sum(3)<
//查看区间[4,6]的和 - //现在给a[5]+100
- up(5,100);
- cout<<sum(6)-sum(3)<
- return 0;
- }

(2)区间修改+单点查询
区间修改用到了前面我们学到的拆分知识,修改区间[L,R]的值,用差分,差分数组的变化时d[l]++,
d[r+1]--;这里同样也是,具体的我们看下代码。
注意点是什么呢,与前面的(1)相比较up的第二个参数第一个是a[i],得出来的sum就是a[i]的前缀和,而在(2)点中,up接受的第二个参数是a[i]-a[i-1],那么sum[i]计算的就是a[i],相当于sum[i]是tree[i]的前缀和,也就是说,第二个参数接受的是什么,sum[i]计算得到的就是第二个参数相加累计得到的。

- #include
- using namespace std;
- #define lowbit(x)((x)&-(x))
- const int N=1e3+10;
- int n,m;
- int a[N];
- int tree[N];
-
- void up(int x,int d)
- {
- while(x<=N){
- tree[x]+=d;
- x+=lowbit(x);
- }
- }
-
- int sum(int x)
- {
- int ans=0;
- while(x>0){
- ans+=tree[x];
- x-=lowbit(x);
- }
- return ans;
- }
-
- int main()
- {
- cin>>n>>m;
- int old=0;
- for(int i=1;i<=n;i++){
- cin>>a[i];
- up(i,a[i]-old);
- old=a[i];
- }
- while(m--){
- int l,r,q;
- cin>>l>>r>>q;
- up(l,q);
- up(r+1,-q);
- }
- //输出a中的所有元素
- for(int i=1;i<=n;i++){
- cout<<sum(i)<<" ";
- }
- return 0;
- }
- /*
- 5 3
- 1 2 3 4 5
- 1 2 3
- 1 1 5
- 3 5 1
- */
(3)区间修改+区间查询
区间查询与区间查询我们就需要两个树状数组了。
原因是什么呢,看下推理公式就知道了。
- a1+a2+a3+....+ak
- =d1+(d1+d2)+(d1+d2+d3)+...+(d1+d2+d3+...+dk)
- =k*d1+(k-1)*d2+(k-2)*d3+...+dk
- =k(d1+d2+...+dk)-(d2+2*d3+...+(k-1)*dk)
- =d*(d1+...+dk)-(i-1)*di(i从1到k)
那么我们创建的两个树状数组一个实现的是d[i],一个实现的是(i-1)*di
代码展现:
- #include
- using namespace std;
- #define lowbit(x)((x)&-(x))
- const int N=1e3+10;
- int a[N];
- int tree1[N],tree2[N];
- int n,m;
-
- void up1(int x,int d)
- {
- while(x<=N){
- tree1[x]+=d;
- x+=lowbit(x);
- }
- }
-
- void up2(int x,int d)
- {
- while(x<=N){
- tree2[x]+=d;
- x+=lowbit(x);
- }
- }
-
- int sum1(int x)
- {
- int ans=0;
- while(x>0){
- ans+=tree1[x];
- x-=lowbit(x);
- }
- return ans;
- }
-
- int sum2(int x)
- {
- int ans=0;
- while(x>0){
- ans+=tree2[x];
- x-=lowbit(x);
- }
- return ans;
- }
-
- int main()
- {
- cin>>n>>m;
- int old=0;
- for(int i=1;i<=n;i++){
- cin>>a[i];
- up1(i,a[i]-old);
- up2(i,(i-1)*(a[i]-old));
- old=a[i];
- }
- while(m--){
- int l,r,q;
- cin>>l>>r>>q;
- up1(l,q);
- up2(l,(l-1)*q);
- up1(r+1,-q);
- up2(r+1,-q*r);
- }
- //查看[3,5]
- cout<<5*sum1(5)-sum2(5)-((3-1)*sum1(3-1)-sum2(3-1));
- return 0;
- }
-
- /*
- 7 3
- 1 2 3 4 5 6 7
- 1 4 2
- 3 6 6
- 1 3 1
- */
