树状数组+例题:树状数组 1 :单点修改,区间查询

本蒟蒻第一次发博客,希望支持。

树状数组

概念

树状数组 (Binary Indexed Tree(BIT)也称作(Fenwick Tree) 是一个区间查询和单点修改复杂度都为log(n)的数据结构。主要用于查询任意两点之间的所有元素之和。

例题

【模板】树状数组 1(洛谷)
树状数组 1 :单点修改,区间查询(Liuser’s OJ)

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某一个数加上 x
  2. 求出某区间每一个数的和

输入格式

第一行包含两个正整数 n,m ,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。
接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:

  1. 含义:将第 x 个数加上 k
1 x k
  1. 含义:输出区间 [x,y] 内每个数的和
2 x y

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

样例

样例1输入
5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4
样例1输出
14
16

数据范围与提示

对于100%的数据,1≤n,m≤5×10^5。

分析

这道题的 常见做法
for(long long i=x;i<=y;i++)
{
 ans+=a[i];
}
//用for循环从区间x到y依次求和,时间复杂度:O(n) 。

缺点:当数据规模极大的时候,将会变得效率低下,容易超时。

这时,我们想到了 前缀和
//原数组为A[i],再定义一个数组B[i],i≤n+5。
B[1]=A[1];
B[2]=A[1]+A[2];
B[3]=A[1]+A[2]+A[3];
……
sum(A[L]+A[L+1]+……+A[R-1]+A[R]) = B[R]-B[L-1]

优点:输入原数组A时,预处理生成B数组,求和时只需一步相减即可。
缺点:若原数组元素A[i] 进行修改后,B[i]和B[i]以后的元素都得改变,那么修改的时间复杂度为O(n),所以时间复杂度相当于没有变,还是O(n)。

这时,怎么办呢?

我们就要使用 树状数组

树状数组+例题:树状数组 1 :单点修改,区间查询_第1张图片
首先,我们要生成树状数组,就如上图生成。
那么这又是怎么生成的呢?

就是通过 Lowbit

Lowbit(i) 的意思是将 i 转化成二进制数之后,只保留最低位的1及其后面的0,截断前面的内容,然后再转成十进制数,这个数也是树状数组中i号位的子叶个数。

long long Lowbit(long long x)
{
 return x&-x;
}
//原数为i(十进制),先将原数转化成二进制之后,在与原数相反数的二进制按位与,答案就是lowbit(i)的结果
对原数组A[i]进行Update(更新)操作
void Update(long long x,long long y)
{
 for(long long i=x;i<=n;i+=Lowbit(i))
 {
  BIT[i]+=y;
 }
 return ;
}
//更新BIT[i]
求Sum(前缀和)操作
long long Sum(long long x)
{
 long long ans=0;
 for(long long i=x;i;i-=Lowbit(i))
 {
  ans+=BIT[i];
 }
 return ans;
}
//求和

代码+注释

#include
using namespace std;

long long a[1000005],BIT[1000005],n,q,s,l,r;
//定义全局数组

long long Lowbit(long long x)
{
 return x&-x;
}
//原数为i(十进制),先将原数转化成二进制之后,在与原数相反数的二进制按位与,答案就是lowbit(i)的结果

void Update(long long x,long long y)
{
 for(long long i=x;i<=n;i+=Lowbit(i))
 {
  BIT[i]+=y;
 }
 return ;
}
//更新BIT[i]

long long Sum(long long x)
{
 long long ans=0;
 for(long long i=x;i;i-=Lowbit(i))
 {
  ans+=BIT[i];
 }
 return ans;
}
//求和 

int main()
{
 scanf("%lld%lld",&n,&q);
 for(long long i=1;i<=n;i++)
 {
  scanf("%lld",&a[i]);
  Update(i,a[i]);
 }
 //输入时预处理,构造BIT[i]
 for(long long i=1;i<=q;i++)
 {
  scanf("%lld%lld%lld",&s,&l,&r);
  if(s==1)
  {
   Update(l,r);
  }
  else
  {
   printf("%lld\n",Sum(r)-Sum(l-1));
   //输出区间[L,R]的和 
  }
 }
 return 0;
}

你可能感兴趣的:(树状数组,c++)