树状数组
树状数组,基于二进制实现快速求前缀和以及修改数组元素。
必要工具:lowbit(x):取x二进制中最后一位1代表的数;
#define lowbit(x) (x & -x)
1.在原数组上建立树状数组
单点修改
add(x,val):使x结点及其所有父节点数值增加val
1 void add(int x,int c){ 2 for(int i=x; i<=n; i+=lowbit(x)) tr[i]+=c; 3 }
区间查询
sum(x):求[1,x]的值
1 int sum(int x){ 2 int res=0; 3 for(int i=x; i; i-=lowbit(x)) res+=tr[i]; 4 return res; 5 }
2.在差分数组上建立树状数组
差分数组
在原数组a[n]上后项与前项差,d[1]=a[1];
for(int i=1; i<=n; i++) d[i]=a[i]-a[i-1];
单点查询
易知a[k]=d[1]+d[2]+...+d[k];即单点查询为查询差分数组上的前缀和。
区间修改
由于差分数组的性质,区间修改 [L.R]+C 只涉及 d[L]+=C 和 d[R+1]-=C,故区间修改相当于单点修改差分数组上的两个点;
add(l,c),add(r+1,-c);
单点修改
区间修改的特殊化
3.同时进行区间修改和区间查询
通过数学计算可知,(a为原数组,d为差分数组)
a[1]+a[2]+...+a[n]
=d[1]+(d[1]+d[2])+...+$\sum_{i=1}^{n}d[i] $
=n*d[1]+(n-1)*d[2]+...+d[n]
=n*$\sum_{i=1}^{n}d[i] $-d[2]-2d[3]-...-(n-1)*d[n]
故我们可以做一个辅助数组c[n],令c[i]=(i-1)*d[i],则区间查询即为n*sum(d,n)-sum(c,n);//对d数组的前缀和乘n减去对c数组的前缀和