树状数组

 树状数组的用途 
树状数组可以解决数量级较大的区间求和问题,区间求最值问题,区间修改、查询问题以及 求逆序对等应用。树状数组的时间复杂度 O(log2n),正常情况下远远快于线性时间 O(n)。 


 树状数组的原理
 

图 2 中,a 数组就是需要维护求区间和的数组,其实a 数组在求区间和的时候并没有太大的 意义,只是求区间最值的时候会有用到。我们先来关注一下储存和的 c 数组。 C8=c4+c6+c7+a8,c6=c5+a6…为什么要这么操作,因为这么操作更加的快捷,如果要查询前 1 到 8 内的区间和,只需要 4 个数相加就 ok,而为什么要选择这 4 个数,因为有一种操作刚 好可以将没一个数按照我们既定的规则将每一位的数加入到 c 数组中。 看一下效果图: 


 
然后看一下标准化的图一,很容易看出其中的二分的思想,其实树状数组就是巧妙的利用了 二分将线性时间降为了 log2n。其余的东西请同学们自行理解。 
 

创建树状数组 根据我们刚才的思想,数组 c 需要记录前 n 项的和,那么就需要将当前项的数加入到后面的 每一个需要用到的数组中,此时实用 lowbit 函数来完成此次操作。 一定要注意数组的下标要从 1 开始,而不是 0.

int lowbit(int x)//巧妙地利用lowbit()函数
{
    return x&(-x);
}
void add(int x,int y)//创建一个树状数组,X为下标,y为数值
{
    while(x<=n)
    {
        c[x]+=y;
        x+=lowbit(x);
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)//注意数组下标要从1开始
        {
            scanf("%d",&a[i]);
            add(i,a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d\n",c[i]);
        }
    }
    return 0;
}


 

 


此时完成树状数组的创建。 
 
区间求和 
 
巧妙利用 lowbit,区间求和是树状数组的一个比较重要的应用。 C数组存的就是区间段的和, 所以求和时只需对 C 数组进行操作,另外求和只是求区间[1,x]的和,如要求[x,y]的和,求出 [1,x-1],[1.y]再相减就 ok。 

 

int read(int x)
{
    int ans=0;
    while(x)
    {
        ans+=c[x];
        x-=lowbit(x);//lowbit()函数之前有提到
    }
    return ans;
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)//注意数组下标要从1开始
        {
            scanf("%d",&a[i]);
            add(i,a[i]);//add()函数之前有提到
        }
      int x,y;
     while(cin>>x>>y)
      {
          printf("%d\n",read(y)-read(x-1));//计算从x到y的区间和
      }

    }

 
 
区间修改 
 

 

 

有时候需要修改一段区间的值,然后再进行区间求和,此时就不能执行以前 C 数组,只能换 一种方法来求。 我们先建立一个数组来储存需要输入的序列,然后创建一个差分数组来保存原数组中每一项 与前一项的差值,这样的话第 i 项就是从第 1 项加到第 i 项的和,我们来看一下前 i 项的和, a[1]+a[2]+...+a[n] = (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])  = n*c[1] + (n-1)*c[2] +... +c[n] = n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])  

 

那么我们就维护一个数组 del2[n],其中 del2[i] = (i-1)*c[i] 

 

然后就成了两个数组的区间求和问题。 代码实现:

 long long Sigma(long long *arra,long long x)   //数组中 1 到 x 的和

 {  

        long long ans=0; 

         while(x) 

         { 

              ans+=arra[x];  

             x-=lowbit(x);

          } 

     return ans; 

} 

void updata(long long *c,long long x,long long y)   //在数组 c 中后 x 加入 y

 {

              while(x<=n) 

             {   

                    c[x]+=y;x+=lowbit(x); 

             } 

} 

for(i=1;i<=10;i++) 

{ 

      cin>>Map[i];

      updata(del1,i,Map[i]-Map[i-1]);

      updata(del2,i,(i-1)*(Map[i]-Map[i-1])); 

} 

while(cin>>q)

 { 

         if(q%2) 

        {  

             cout<<"区间修改,输入要修改的区间以及要添加的值:"<<endl;  

             cin>>x>>y>>z;  

              updata(del1,x,z);

              updata(del1,y+1,-z);  

               updata(del2,x,z*(x-1));

                updata(del2,y+1,-z*y); 

     }  

else  {   

           cout<<"区间查询,输入要查询的区间:"<<endl;  

           cin>>x>>y; 

           long long sum1=(x-1)*Sigma(del1,x-1)-Sigma(del2,x-1);    

           long long sum2=y*Sigma(del1,y)-Sigma(del2,y);     

           printf("%lld\n",sum2-sum1);

     }

 } 

 
效果图: 
 
 

 void query_max(int l,int r)

 {    

         long long ans=Map[r];   

          while(true)  {      

           ans=max(ans,Map[r]); 

                if(r==l)   

                         break;    

         for(r-=1;r-l>=lowbit(r);r-=lowbit(r))  

       {         

          ans=max(ans,maxx[r]);  

           }  

   }    

             cout<<"最大值为:"<<ans<<endl;

 } 

 

void query_min(int l,int r)

 {    

     long long ans=Map[r]; 

      while(true)  { 

       ans=min(ans,Map[r]);   

      if(r==l)    

               break;  

    for(r-=1;r-l>=lowbit(r);r-=lowbit(r))  

    {      

         ans=min(ans,minn[r]);    

    }   

 }   

  cout<<"最小值为:"<<ans<<endl;

 } //主函数 

for(i=1;i<=10;i++)  {  

    maxx[i]=Map[i];  

    for(int j=1;j<lowbit(i);j<<=1)  

   {       

    maxx[i]=max(maxx[i],maxx[i-j]);  

    } 

 }  

for(i=1;i<=10;i++)  {

      minn[i]=Map[i];   

   for(int j=1;j<lowbit(i);j<<=1) 

    { 

         minn[i]=min(minn[i],minn[i-j]); 

     }

}  

while(cin>>x>>y)  {     

    query_max(x,y); 

     query_min(x,y);

 } 


 
 
求最值 
 
参考博客:https://www.cnblogs.com/ambition/archive/2011/04/06/bit_rmq.html 
 

代码实现:

 
效果图: 
 
 
The end……

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值