二维树状数组
二维树状数组
本篇博客旨在实现一种玄妙的黑科技:二维树状数组,支持区间修改和区间查询
虽然这个东西叫做树状数组,但其实和普通树状数组除了\(lowbit\)之外大约没什么特别相似的东西……
我们考虑,普通的树状数组中,我们用\(t[x]\)维护了右端点为\(x\),长度为\(lowbit(x)\)的区间的和,那么同理,在二维树状数组中,我们用\(t[x][y]\)维护左上角顶点为\((x,y)\),大小为\(lowbit(x)*lowbit(y)\)的区间的和
我们知道,树状数组的区间操作实际上依托于差分和前缀和。那么同理,我们尝试利用二维差分和前缀和来维护二维树状数组
具体过程如下:
区间修改
我们回忆一下我们是如何对一维树状数组进行区间修改的。我们对其进行差分操作,是为了使得到的差分数组前缀和就等于对应位置元素的值。那么我们可以运用类比思想,设计一个二维的差分
我们知道二维前缀和具有计算公式:
\(sum[i][j]=sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1]+a[i][j]\)
我们又知道差分是前缀和的逆运算,那么我们可以得到对于区间加,存在差分操作:
假设需要修改的区间为\([(x1,y1),(x2,y2)]\),则
\(t[x1][y1]+k,t[x2+1][y2+1]+k,t[x1][y2+1]+k,t[x2+1][y1]+k\)
区间查询
根据差分数组的定义,我们不难发现,对于点\((x,y)\),它的二维前缀和就是:
\(\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}\sum\limits_{h=1}^{i}\sum\limits_{k=1}^{j}t[h][k]\)
但我们类比一下一维树状数组的区间求和,我们亦可以统计每个\(d[h][k]\)出现的次数,我们就可以发现\(d[1][1]\)出现了\((x×y)\)次,\(d[1][2]\)出现了\(x×(y−1)\)次……\(d[h][k]\)出现了\((x−h+1)\times(y−k+1)\)次
则原式整理得:
\(\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}t[h][k]\times(x-i+1)\times(y-j+1)\)
分解并整理,得到:
\(\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}(t[h][k]\times(xy+x+y+1)-t[h][k]\times{i}\times(y+1)-t[h][k]\times{j}\times(x+1))+t[h][k]\times{i}\times{j}\)
根据我们最后分解出来的公式,我们需要维护四个数组\(d[i][j],d[i][j]\times{i},d[i][j]\times{j},d[i][j]\times{i}\times{j}\),从而实现区间查询
例题 P4514 上帝造题的七分钟
朴素二维树状数组,支持操作区间加、区间查询。代码如下:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 3000
#define M 200010
#define lowbit(x) x&(-x)
int n,m,a,b,c,d,k;
char op[2];
/*二维树状数组模板*/
struct BIT{
int t[N][N];
void add(int xi,int xj,int k){
for(int i=xi;i<=n;i+=lowbit(i)){
for(int j=xj;j<=m;j+=lowbit(j)){
t[i][j]+=k;
}
}
return;
}
int ask(int xi,int xj){
int sum=0;
for(int i=xi;i>=1;i-=lowbit(i)){
for(int j=xj;j>=1;j-=lowbit(j)){
sum+=t[i][j];
}
}
return sum;
}
}T[4];
void Add(int x,int y,int k){
T[0].add(x,y,k);
T[1].add(x,y,k*x);
T[2].add(x,y,k*y);
T[3].add(x,y,k*x*y);
}
void modify(int a,int b,int c,int d,int k){
Add(a,b,k);
Add(c+1,b,-k);
Add(a,d+1,-k);
Add(c+1,d+1,k);
//二维差分
}
int Ask(int x,int y){
int res=0;
res+=T[0].ask(x,y)*(x*y+x+y+1);
res-=T[1].ask(x,y)*(y+1);
res-=T[2].ask(x,y)*(x+1);
res+=T[3].ask(x,y);
return res;
}
int query(int a,int b,int c,int d){
int res=0;
res+=Ask(c,d);
res+=Ask(a-1,b-1);
res-=Ask(c,b-1);
res-=Ask(a-1,d);
return res;
//二维前缀和
}
signed main(){
scanf("X %d %d",&n,&m);
while(~scanf("%s",&op)){
scanf("%d%d%d%d",&a,&b,&c,&d);
if(op[0]=='L'){
scanf("%d",&k);
modify(a,b,c,d,k);
}else{
printf("%d\n",query(a,b,c,d));
}
}
return 0;
}

浙公网安备 33010602011771号