上一篇讲的是一维数组的树状数组,可以实现“单点修改,区间查询”,“区间修改,单点查询”,“区间修改,区间查询”。
今天接触二维树状数组。
其实,要明确的一点是,不管是一维还是二维树状数组,都只是工具而已,只是帮助我们更快地求和,查询,树状数组的这些操作都可以用我们平常的方法求,例如一直加。
面对一个二维数组,我们要求它们的和,会怎么做呢?
先求出第一行的总和
再求出第二行的总和
再求出第三行的总和
..........
求出最后一行的总和
最后就是每一行都加起来,得出来的就是二维数组的总和。
那我们用树状数组来帮我们 “算快一点” 呢?
第一行是一个树状数组,区间求和
第二行是一个树状数组,区间求和
第三行是一个树状数组,区间求和
..........
最后一行也是树状数组,区间求和
假如有 100000 行呢?
那我们把每一行树状数组当做一个值,变成一列,对这一列求和,那不就变成一个一维树状数组了吗
int sum( int x , int y ){
int _y = y ;
LL ans = 0 ;
while( x ){
_y = y ;
while( _y ){
ans += c[x][_y] ;
_y -= lower( _y ) ;
}
x -= lower( x ) ;
}
return ans ;
}
这是对 1- x , 1-y 所表示的矩阵的求和函数,就是嵌套的一维求和呢
换个方式看
LL sum( int *c , int x ){
LL ans = 0 ;
while( x ){
ans += c[x] ; // 每一行求和
x -= lower( x ) ;
}
return ans ;
}
int Matrix_sum( int x , int y ){
LL ans = 0 ;
while( x ){
ans += sum( c[x] , y ) ; // 所有行当做一列,对这一列求和
x -= lower( x ) ;
}
return ans ;
}
应该很清楚了吧
更新操作一样的,也都是一维树状数组的更新,然后每一行再看做一列,还是一维更新
void update( int x , int y , int add ){
int _y = y ;
while( x <= N ){
_y = y ;
while( _y <= N ){
c[x][_y] += add ;
_y += lower( _y ) ;
}
x += lower( x ) ;
}
}
换个方式看void update( int *c , int x , int add ){
while( x <= N ){
c[x] += add ;
x += lower( x ) ;
}
}
void Matrix_update( int x , int y , int add ){
while( x <= N ){
update( c[x] , y , add ) ;
x += lower( x ) ;
}
}
其实都是我们正常对矩阵的求和操作,先求出每一行,再求这些行的总和。树状数组,线段树只是让我们求得更快而已。
也就可以推广到三维,四维..... N 维,都是树状数组的嵌套。
贴一道例题:POJ 2155
对二维数组的求和,求出来的和,如果是偶数,说明被修改回来了,如果是奇数,说明没被改回来
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std ;
#define LL long long
#define lower(x) x&-x
int N , Q ;
int c[1002][1002] ;
void update( int x , int y , int add ){
int _y = y ;
while( x <= N ){
_y = y ;
while( _y <= N ){
c[x][_y] += add ;
_y += lower( _y ) ;
}
x += lower( x ) ;
}
}
int sum( int x , int y ){
int _y = y ;
LL ans = 0 ;
while( x ){
_y = y ;
while( _y ){
ans += c[x][_y] ;
_y -= lower( _y ) ;
}
x -= lower( x ) ;
}
return ans & 1 ;
}
int main(){
int cases , x1 , y1 , x2 , y2 ;
scanf( "%d" , &cases ) ;
while( cases-- ){
memset( c , 0 , sizeof( c ) ) ;
scanf( "%d%d" , &N , &Q ) ;
char opt[2] ;
while( Q-- ){
scanf( "%s" , opt ) ;
if( opt[0] == 'C' ){
scanf( "%d%d%d%d" , &x1 , &y1 , &x2 , &y2 ) ;
update( x1 , y1 , 1 ) ;
update( x1 , y2+1 , -1 ) ;
update( x2+1 , y1 , -1 ) ;
update( x2+1 , y2+1 , 1 ) ;
}
else{
scanf( "%d%d" , &x1 , &y1 ) ;
printf( "%d\n" , sum( x1 , y1 ) ) ;
}
}
if( cases ) printf( "\n" ) ;
}
return 0 ;
}