树状数组的扩展

一、基础树状数组

(一)前缀和实现(单点修改,区间查询)

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个数组更新,查询时带上系数)

posted @ 2019-11-12 21:05  Robert_JYH  阅读(188)  评论(0编辑  收藏  举报