【笔记】树状数组
树状数组可以高效率地查询和维护数列的前缀和(或区间和),支持单点修改和区间查询。
根据仍以正整数关于
若一个正整数
- 长度为
2^{i1} 的小区间[1,2^{i1}] - 长度为
2^{i2} 的小区间[2^{i1}+1,2^{i1}+2^{i2}] -
\cdots - 长度为
2^{im} 的小区间[2^{i1}+2^{i2}+\cdots+2^{im-1}+1,2^{i1}+2^{i2}+\cdots+2^{im}]
其中,若区间结尾为 lowbit(R)。
由二进制得 lowbit(x)=x&(~x+1)=x&(-x)。因为取反后,末尾的所有 -x=~x+1,所以还可以进一步简化。
int lowbit(int x) {return x&(-x);}
int main()
{
cin >> x;
for (int i = x; i; i -= lowbit(i))
//分成的每一个区间为 [i-lowbit(i)+1, i]
}
树状数组基于此,可以维护序列前缀和。对于给定的序列
由此,我们写出树状数组的查询前缀和代码。
ll query(int x)
{//查询[1,x]区间和
ll ans=0;
for(;x>0;x-=x&(-x)) ans += a[x]; //拆区间,计算和
return ans;
}
事实上,树状数组中,结点
由此写出树状数组的单点修改代码。
void add(int x, int y)
{//将 a[x] 加上 y
for (;x<=n;x+=x&(-x)) a[x]+=y;//不断修改更新并跳向父亲,直至超出数组长度n
}