给你一个数组a[],求一段连续区间的和;
解:可以用sum[i]表示a[1]到a[i]的和,这样求a[l]到a[r]的和就变成了求sum[r]-sum[l-1],预处理O(n)的时间复杂度处理出sum[]之后查询只需要O(1)
(注释:把l-r的问题转化为1-l,1-r的问题是一种常用思想)
但是如果加入另外一个操作给a[i]加上x,发现,如果给第一个元素加上x之后全部元素都会加上x,时间复杂度就会很大
那么应该怎么办?
修改和查询一个操作是O(1),一个操作是O(n)(这不是数组和链表吗)
所以可以用分块的思想分成根号n块,每块记录他的前缀和,这样查询和修改都变成了根号n的时间复杂度
但是这种分配方式并不算好,有没有更好的分配方式呢?
树状数组:
在树状数组中sum[i]代表的是a[i-lowbit(i)]到a[i]的和
lowbit(i)是i在二进制中最后一位1,例如i是二进制数10100,那么lowbit(i)=4(二进制100)
那么可以得到求前n项的和是只有求二进制位为一的项数之和,为1的位数不超过logn所以查询的时间复杂度为logn
同理修改操作也只会影响logn个位置的值,那么修改的操作也是logn的时间复杂度
int lowbit(int x){
return x&(-x);
}
void add(int x,int y){//给a[x]+y
while(x<=n){
sum[x]+=y;
x+=lowbit(x);
}
}
int qian_zhui_he(int x){//求a[1]到a[x]的和
int ans=0;
while(x>0){
ans+=sum[x];
x+=lowbit(x);
}
return ans;
}
知识兔注释:如果a[]初始数组不全为0,那么给每个点做一次add操作即可