【学习笔记】二维树状数组
前置芝士
-
一维树状数组之区间修改、区间查询
-
二维差分、二维前缀和
-
知道位置 i 管辖的范围为 \(i-lowbit(i)+1 \sim i\) ,父亲节点为 \(i+lowbit(i)\)
二维树状数组
单点修改,区间查询
思路
解决方案十分暴力,直接在一维树状数组上再套一维即可。
不必思考这棵树具体长什么样子,如果一定要知道就是在一维树状数组的每个节点内部再套一个树状数组(不一定对,但是确实没用)。
代码
inline int lowbit(int x){
return x&(-x);
}
inline void change(int x,int y,int k){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
C[i][j]+=k;
return;
}
inline int query(int x,int y){
int res=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
res+=C[i][j];
return res;
}
区间修改,单点查询
思路
仍然可以利用一维数组的核心思想——差分,仍然十分暴力,只需要利用二维差分进行修改即可。
代码
inline int lowbit(int x){
return x&(-x);
}
inline void change(int x,int y,int k){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
C[i][j]+=k;
return;
}
inline void change_s(int x1,int y1,int x2,int y2,int k){
change(x1,y1,k);
change(x1,y2+1,-k);
change(x2+1,y1,-k);
change(x2+1,y2+1,k);
return;
}//(x1,y1)是左上角,另一个是右下角
inline int query(int x,int y){
int res=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
res+=C[i][j];
return res;
}//这里查询以后的结果是(x,y)这一个位置的值
区间修改,区间查询
思路
与一维树状数组相似,我们并不打算放弃区间修改的差分思想,所以只好将区间查询进行整活。
对于查询的区间 \((1,1) \sim (x,y)\) ,易知答案为 \(\sum\limits^x_{i=1}\sum\limits^y_{j=1}{差分数组上的(1,1) \sim (i,j)}\) ,也就是 \(\sum\limits^x_{i=1}\sum\limits^y_{j=1}\sum\limits^i_{k=1}\sum\limits^j_{l=1}C[k][l]\) 。
同样是根据一维树状数组的思路,可以利用可以推算出的每个元素出现的次数,将上式化为 \(\sum\limits^x_{i=1}\sum\limits^y_{j=1}C[i][j] \times (x+1-i) \times (y+1-j)\) 。
再根据多项式乘法展开上式,可以得到
\[(x+1) \times (y+1) \times \sum\limits^x_{i=1}\sum\limits^y_{j=1}C[i][j] - (y+1) \times \sum\limits^x_{i=1}\sum\limits^y_{j=1}C[i][j] \times i - (x+1) \times \sum\limits^x_{i=1}\sum\limits^y_{j=1}C[i][j] \times j +\sum\limits^x_{i=1}\sum\limits^y_{j=1}C[i][j] \times i \times j
\]
于是,和一维树状数组相同地,只需要开四个树状数组维护 \(C[i][j]\),\(C[i][j] \times i\),\(C[i][j]\times j\) 和\(C[i][j]\times i \times j\) 即可。
代码
inline int lowbit(int x){
return x&(-x);
}
inline void change(int x,int y,int k){
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
C[i][j]+=k,
Ci[i][j]+=k*x,
Cj[i][j]+=k*y,
Cij[i][j]+=k*x*y;
return;
}
inline void change_s(int x1,int y1,int x2,int y2,int k){
change(x1,y1,k);
change(x1,y2+1,-k);
change(x2+1,y1,-k);
change(x2+1,y2+1,k);
return;
}//(x1,y1)是左上角,另一个是右下角
inline int query(int x,int y){
int res=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
res+=(x+1)*(y+1)*C[i][j]-(y+1)*Ci[i][j]-(x+1)*Cj[i][j]+Cij[i][j];
return res;
}