树状数组

Posted SummerSky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组相关的知识,希望对你有一定的参考价值。

这是一种实用并且代码极短的高级数据结构。

能在O(lgn)内完成修改,和询问。解决了普通数组的询问长,前缀和的修改长的问题。

它提供两种操作:

  • 将A[i]叫上D;
  • 求出A[i]的前缀和。

那么怎么实现呢?

我们新增一个数组c[],其中c[i]=A[i-2^k+1]+……+A[i](k为i在二进制形式下末尾0的个数)。

那怎么求出2^k呢,我们就可以用lowbit(i),lowbit(i)=i&(-i);

首先是修改操作,将所有包含它的数都加上要加的数。

技术分享
1 void add(int x){
2     for (int i=x;i<=N;i+=lowbit(i)) c[i]++;
3 }
View Code

求和就是把它未包含的数加上。

技术分享
1 long long sum(int x){
2     long long Sum=0;
3     for (int i=x;i>0;i-=lowbit(i)) Sum+=c[i];
4     return Sum;
5 }
View Code

这就是基本的树状数组了。

接下来是一些基本的求和模型。

1.改点求段:

  修改操作:将A[x]的值加上v;

  求和操作:求此时A[l..r]的和。

十分简单:

技术分享
1 void add(int x){
2     for (int i=x;i<=N;i+=lowbit(i)) c[i]++;
3 }
4 long long sum(int x){
5     long long Sum=0;
6     for (int i=x;i>0;i-=lowbit(i)) Sum+=c[i];
7     return Sum;
8 }
View Code

  修改操作:add(x,v);

  求和操作:sum(r)-sum(l-1);

s2.改段求点:

  修改操作:将A[l..r]之间的全部元素值加上v;

  求和操作:求此时A[x]的值。

这回就用差分发A[i]表示原意义一下的A[i]-A[i-1];

技术分享
1 void add(int x){
2     for (int i=x;i<=N;i+=lowbit(i)) c[i]++;
3 }
4 long long sum(int x){
5     long long Sum=0;
6     for (int i=x;i>0;i-=lowbit(i)) Sum+=c[i];
7     return Sum;
8 }
View Code

  修改操作:add(l,v),add(r+1,-v);

  求和操作:sum(x);

3.改段求段:

  修改操作:将A[l..r]之间的全部元素值加上v;

  求和操作:求此时A[l..r]的和。

关于这种模型需要一个辅助数组:存(当前位置-1)*v的值。

因为A[1..x]的和在查差分意义下是(sum(a[i]*(x-(i-1))))(i<=x)

所以将x*(sum(x))-sum辅助数组(x)就是A[1..x]的和。

技术分享
 1 void Add(ll *T,int x,ll v){
 2     for (int i=x;i<=n;i+=lowbit(i)) T[i]+=v; 
 3 }
 4 void Update(ll a,ll b,ll c){
 5     Add(T1,a,c); Add(T1,b+1,-c);
 6     Add(T2,a,(a-1)*c); Add(T2,b+1,b*(-c));
 7 }
 8 ll Sum_(ll *T,int x){
 9     ll ans=0;
10     for (int i=x;i>0;i-=lowbit(i)) ans+=T[i];
11     return ans;
12 }
13 ll Sum(int a,int b){
14     ll tot=(a-1)*(Sum_(T1,a-1))-Sum_(T2,a-1);
15     ll tot_=(b)*(Sum_(T1,b))-Sum_(T2,b);
16     return tot_-tot;
17 }
View Code

  修改操作:模板内Update;

  求和操作:模板内Sum;

还有二维树状数组的使用,

技术分享
 1 int sum(int x,int y)  
 2 {  
 3     int Sum=0;  
 4     for(int i=x;i>0;i-=(i&(-i))) {  
 5         for(int j=y;j>0;j-=(j&(-j))) {  
 6             Sum+=c[i][j];  
 7         }  
 8     }  
 9     return Sum;  
10 }  
11   
12 void add(int x,int y,int d)  
13 {  
14     for(int i=x;i<=n;i+=(i&(-i))) {  
15         for(int j=y;j<=n;j+=(j&(-j))) {  
16             c[i][j]+=d;  
17         }  
18     }  
19 }  
View Code

 


以上是关于树状数组的主要内容,如果未能解决你的问题,请参考以下文章

数据结构之树状数组从零认识树状数组

树状数组和线段树有啥区别?

树状数组

树状数组

树状数组

如何利用树状数组修改一个区间?