使用情况: 1. 单点增减修改求区间总和(我愿称之为升级版前缀和)
2.区间增减修改求单点
程序实现:
(1) 单点增减修改求区间总和:
原理:前缀和是用c[n]记录 1 ~ n 之和,而树状数组则相当于建了一棵树,
而c[n]记录n这个节点为根的子树中所有节点的和。
操作:为了建一棵树并在树上进行修改,我们需要引入一个概念:lowbit
lowbit是指一个二进制数的最低位的1的权值。
例:lowbit(1) = 1 , lowbit(2)= 2 , lowbit(3) = 1 , lowbit(4)= 4
然后,我们可以利用lowbit(n)来表示以n为根节点的子树的叶子节点的个数,
并以此为基础建树。
其中,c[n]记录 n - lowbit(n) +1 ~ n 的格子之和。
接下来我们要处理如何更新这棵树的问题。
假如我们要将第二格的格子+2,那么会影响C[2] , C[4]。
那么,当我们Cn时,我们要继续把后面的C[n+lowbit(n)]也更新
直到n+lowbit(n)超过范围。
最后我们处理区间总和,假如我们要求[L,R]范围内的和,
那么我们可以用前缀和[1,R] - [1,L]来求。
现在我们的问题变为了如何求[1,n]。
假如我们要求[1,4],[1,4] = C4
假如我们要求[1,5],[1,5] = C5 + C4
我们只要一直加前面子树的总和即可,而我们可用n - lowbit(n)跟踪前面子树的位置。
参考程序:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
int a[500005],b[500005],c[500005];
inline int lowbit(int x){return x&(-x);}
void update(int i,int k)
{
while(i <= n)
{
c[i] += k;
i += lowbit(i);
}
}
int Sum(int i)
{
int sum1 = 0;
while(i > 0)
{
sum1 += c[i];
i -= lowbit(i);
}
return sum1;
}
int main()
{
cin>>n>>m;
a[0] = 0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
update(i,a[i]);
}
for(int i=1;i<=m;i++)
{
int f;
cin>>f;
if(f == 1)
{
int x,k;
cin>>x>>k;
update(x,k);
}
else if(f == 2)
{
int L,R;
cin>>L>>R;
cout<<Sum(R) - Sum(L-1)<<endl;
}
}
return 0;
}