一、基础树状数组
(一)前缀和实现(单点修改,区间查询)
inline int lowbit(int x){return x&-x;} inline void change(int x,int k){ while(x<=n){ tree[x]+=k; x+=lowbit(x); } } inline ll query(int x){ ll ans=0; while(x){ ans+=tree[x]; x-=lowbit(x); } return ans; }
(二)差分实现(区间修改,单点查询)
区间修改,单点查询在修改时维护一个差分即可
change(x,k); change(y+1,-k);
二、区间树状数组(区间修改,区间查询)
考虑\([1,r]\)的区间和
令\(tree[i]=a[i]-a[i-1]\),我们有\(a[i]=\sum_\left.k=1\right.^i tree[k]\)
则
\[
\sum_\left.i=1\right.^ra[i]=\sum_\left.i=1\right.^r\sum_\left.k=1\right.^i tree[k]=\sum_\left.i=1\right.^\left.r\right.tree[i]*(r-i+1)=r*\sum_\left.i=1\right.^rtree[i]-\sum_\left.i=1\right.^\left.r\right.tree[i]*(i-1)
\]
令\(cf[i]=tree[i]*(i-1)\),则我们要求
\[
\sum_\left.i=1\right.^ra[i]=r*\sum_\left.i=1\right.^rtree[i]-\sum_\left.i=1\right.^\left.r\right.cf[i]
\]
//初值 for(int i=1;i<=n;i++){ scanf("%lld",&x); change(tree,i,x-now); change(cf,i,(i-1)*(x-now)); now=x; } //区间修改 区间为[x,y] change(tree,x,k); change(tree,y+1,-k); change(cf,x,k*(x-1)); change(cf,y+1,-k*y); //区间查询 区间为[x,y] ll sum1=y*query(tree,y)-(x-1)*query(tree,x-1); ll sum2=query(cf,y)-query(cf,x-1); printf("%lld\n",sum1-sum2);
Update一种更优秀的写法
inline int lowbit(int x){return x&-x;} void change(int x,ll k){ ll p=x; while(x<=n){ tree[x]+=k; cf[x]+=(p-1)*k; x+=lowbit(x); } } ll query(int x){ ll ans=0,p=x; while(x){ ans+=p*tree[x]-cf[x]; x-=lowbit(x); } return ans; } //修改 change(x,k); change(y+1,-k); //查询 printf("%lld\n",query(y)-query(x-1));
三、高维树状数组
(一)前置技能
更多可参见 https://www.cnblogs.com/Miracevin/p/9778266.html
前缀和及差分可利用容斥解决,不再详细展开
高维前缀和就是求一个集合子集状态的和
二维前缀和
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
或
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]+=a[i][j-1]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]+=a[i-1][j];
即为前缀和逆过来
高维二元素前缀和(w表示最高维度)
for(int i=0;i<w;i++) for(int j=0;j<(1<<w);j++) if(j&(1<<i))f[j]+=f[j^(1<<i)];
(二)基础二维树状数组
单点修改,区间查询
inline int lowbit(int x){return x&(-x);} void change(int x,int y,ll k){ for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j)) tree[i][j]+=k; } ll query(int x,int y){ ll ans=0; for(int i=x;i>=1;i-=lowbit(i)) for(int j=y;j>=1;j-=lowbit(j)) ans+=tree[i][j]; return ans; } //查询 query(x2,y2)-query(x_1-1,y2)-query(x2,y_1-1)+query(x_1-1,y_1-1)
(三)区间二维树状数组
类似一维情况,可得到
\[
\sum_\left.i=1\right.^x\sum_\left.j=1\right.^ya[i][j]=x*y\sum_\left.i=1\right.^x\sum_\left.j=1\right.^ytree[i][j]-y*\sum_\left.i=1\right.^x\sum_\left.j=1\right.^ytree[i][j]*(i-1)\\-x*\sum_\left.i=1\right.^x\sum_\left.j=1\right.^ytree[i][j]*(j-1)+\sum_\left.i=1\right.^x\sum_\left.j=1\right.^ytree[i][j]*(i-1)*(j-1)
\]
类似于容斥,可推广到n维情况
代码CSP之后再更新(维护4个数组更新,查询时带上系数)