树状数组的扩展
一、基础树状数组
(一)前缀和实现(单点修改,区间查询)
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个数组更新,查询时带上系数)