树状数组

树状数组,基于二进制实现快速求前缀和以及修改数组元素。

 

必要工具: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数组的前缀和

 

posted @ 2019-10-19 21:33  时侍  阅读(85)  评论(0)    收藏  举报