树状数组
一维树状数组
用法一:单点修改,区间查询
单点修改:add(bit,x,v)
区间查询:query(bit,r)-query(bit,l-1)
用法二:区间修改,单点查询
区间修改:add(bit,l,v),add(bit,r+1,-v)
单点查询:query(bit,x)
int limit;//树状数组下标的上界
void add(ll *bit,int x,ll v){
while(x<=limit)bit[x]+=v,x+=x&-x;
}
ll query(ll *bit,int x){
ll res=0;
while(x)res+=bit[x],x-=x&-x;
return res;
}
用法三:区间修改,区间查询
令数组 a a a 的差分数组为 d d d ,即 a [ n ] = ∑ i = 1 n d [ i ] a[n]=\sum\limits_{i=1}^{n}{d[i]} a[n]=i=1∑nd[i]
则 ∑ i = 1 r a [ i ] = ∑ i = 1 r ∑ j = 1 i d [ j ] \sum\limits_{i=1}^{r}{a[i]}=\sum\limits_{i=1}^{r}{\sum\limits_{j=1}^{i}{d[j]}} i=1∑ra[i]=i=1∑rj=1∑id[j] = ∑ i = 1 r ( r + 1 − i ) ∗ d [ i ] =\sum\limits_{i=1}^{r}{(r+1-i)*d[i]} =i=1∑r(r+1−i)∗d[i] = ( r + 1 ) ∗ ∑ i = 1 r d [ i ] − ∑ i = 1 r d [ i ] ∗ i =(r+1)*\sum\limits_{i=1}^{r}{d[i]}-\sum\limits_{i=1}^{r}{d[i]*i} =(r+1)∗i=1∑rd[i]−i=1∑rd[i]∗i
用 b i t 1 bit1 bit1 来维护 d [ i ] d[i] d[i] 的前缀和,用 b i t 2 bit2 bit2 来维护 d [ i ] ∗ i d[i]*i d[i]∗i 的前缀和
void real_add(ll *bit1,ll *bit2,int l,int r,ll v){
add(bit1,l,v);
add(bit2,l,v*l);
add(bit1,r+1,-v);
add(bit2,r+1,-v*(r+1));
}
ll real_query(ll *bit1,ll *bit2,int l,int r){
return (r+1)*query(bit1,r)-query(bit2,r)-l*query(bit1,l-1)+query(bit2,l-1);
}
用法四:求第一个前缀和大于等于某个值的下标
int sum_lower_bound(ll *bit,ll v){
int pos=0;
for(int i=20;i>=0;i--)
if(pos+(1<<i)<=limit&&bit[pos+(1<<i)]<v)
v-=bit[pos|=1<<i];
return pos+1;
}
二维树状数组
用法一:单点修改,区间查询
单点修改:add(x,y,v)
区间查询:query(x2,y2)+query(x1-1,y1-1)-query(x1-1,y2)-query(x2,y1-1)
用法二:区间修改,单点查询
区间修改:add(x1,y1,v),add(x2+1,y2+1,v),add(x1,y2+1,-v),add(x2+1,y1,-v)
单点查询:query(x,y)
const int SIZE=5005;
int limit1=SIZE-1,limit2=SIZE-1;//两个维度的下标的上界
ll bit[SIZE][SIZE];
void add(int x,int y,ll v){
for(int i=x;i<=limit1;i+=i&-i)
for(int j=y;j<=limit2;j+=j&-j)
bit[i][j]+=v;
}
ll query(int x,int y){
ll res=0;
for(int i=x;i;i-=i&-i)
for(int j=y;j;j-=j&-j)
res+=bit[i][j];
return res;
}
用法三:区间修改,区间查询
令数组 a a a 的二维差分数组为 d d d ,即 a [ n ] [ m ] = ∑ i = 1 n ∑ j = 1 m d [ i ] [ j ] a[n][m]=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}{d[i][j]} a[n][m]=i=1∑nj=1∑md[i][j]
则 ∑ i = 1 x ∑ j = 1 y a [ i ] [ j ] = ∑ i = 1 x ∑ j = 1 y ∑ k = 1 i ∑ t = 1 j d [ k ] [ t ] \sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{a[i][j]}=\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}\sum\limits_{k=1}^{i}\sum\limits_{t=1}^{j}{d[k][t]} i=1∑xj=1∑ya[i][j]=i=1∑xj=1∑yk=1∑it=1∑jd[k][t] = ∑ i = 1 x ∑ j = 1 y ( x + 1 − i ) ( y + 1 − j ) d [ i ] [ j ] =\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{(x+1-i)(y+1-j)d[i][j]} =i=1∑xj=1∑y(x+1−i)(y+1−j)d[i][j]
= ( x + 1 ) ( y + 1 ) ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] =(x+1)(y+1)\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]} =(x+1)(y+1)i=1∑xj=1∑yd[i][j] − ( y + 1 ) ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] ∗ i -(y+1)\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]*i} −(y+1)i=1∑xj=1∑yd[i][j]∗i − ( x + 1 ) ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] ∗ j -(x+1)\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]*j} −(x+1)i=1∑xj=1∑yd[i][j]∗j + ∑ i = 1 x ∑ j = 1 y d [ i ] [ j ] ∗ i j +\sum\limits_{i=1}^{x}\sum\limits_{j=1}^{y}{d[i][j]*ij} +i=1∑xj=1∑yd[i][j]∗ij
用四个树状数组分别维护 d [ i ] [ j ] d[i][j] d[i][j] , d [ i ] [ j ] ∗ i d[i][j]*i d[i][j]∗i , d [ i ] [ j ] ∗ j d[i][j]*j d[i][j]∗j , d [ i ] [ j ] ∗ i j d[i][j]*ij d[i][j]∗ij 的前缀和
const int SIZE=5005;
int limit1=SIZE-1,limit2=SIZE-1;//两个维度的下标的上界
ll bit1[SIZE][SIZE],bit2[SIZE][SIZE],bit3[SIZE][SIZE],bit4[SIZE][SIZE];
void add(int x,int y,ll v){
for(int i=x;i<=limit1;i+=i&-i)for(int j=y;j<=limit2;j+=j&-j){
bit1[i][j]+=v;
bit2[i][j]+=v*x;
bit3[i][j]+=v*y;
bit4[i][j]+=v*x*y;
}
}
ll query(int x,int y){
ll res=0;
for(int i=x;i;i-=i&-i)
for(int j=y;j;j-=j&-j)
res+=(x+1)*(y+1)*bit1[i][j]-(y+1)*bit2[i][j]-(x+1)*bit3[i][j]+bit4[i][j];
return res;
}
void real_add(int x1,int y1,int x2,int y2,ll v){
add(x1,y1,v);
add(x1,y2+1,-v);
add(x2+1,y1,-v);
add(x2+1,y2+1,v);
}
ll real_query(int x1,int y1,int x2,int y2){
return query(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1);
}