当要频繁的对数组元素进行修改,同时又要频繁的查询数组内任一区间元素之和的时候,可以考虑使用树状数组
定义:a[1...N]为原数组,c[1...N]为对应的树状数组;
其中:用c[i]表示从数据数组a中某一处一直到a[i]共2^k个元素的总和 即a[i-2^k+1]~a[i]
2^K就是i的二进制表示中倒数第一个1以及之后的0(比如(1001000)2的2^k就是(1000)=8
低位(LowBit)
LowBit(x) = x&-x
修改 (Modify)
如果我们修改了a[i],我们只需对c[i],c[i+lowbit(i)],c[i+lowbit(i)+lowbit(i+lowbit(i))]……进行修改,复杂度O(logn)
void modify(int i,int k)
{
for(;i <= n;i += i&-i)c[i] += k;// 直接lowbit了
}
求和 (Sum)原理:a[1]+...+a[i]=c[i]+c[i-lowbit(i)]+c[i-lowbit(i)-lowbit(i-lowbit(i))]……复杂度O(logn)
这种处理方式等效与每次将i二进制的1末尾删去一个1,那么就刚好跳出这个i管辖的区间而达到分段计值的目的
int Sum(int i)
{
int ans = 0;
for(;i;i -= i&-i)ans += c[i];
return ans;
}
查询a[i]...a[j],则可调用sum(j)-sum(i-1)
实现代码:
#include <cstdio>
const int N = 500000;
int a[N],c[N],n,m;
void add(int i,int k)
{
for(;i <= n;i += i&-i)c[i] += k;
}
int sum(int i)
{
int ans = 0;
for(;i;i -= i&-i)ans += c[i];
return ans;
}
int main()
{
int i,j,k;
scanf("%d%d",&n,&m);
for(i = 1;i <= n;i++)scanf("%d",a+i),add(i,a[i]);
while(m--)
{
scanf("%d%d%d",&i,&j,&k);
if(i)add(j,k-a[j]);
else
{
if(k == j){printf("%d",a[k]);continue;}
printf("%d\n",sum(k) - sum(j-1));
}
}
return 0;
}