多段树状数组
固定总长的多棵树状数组
概述
有时候,我们需要很多棵树状数组,每棵尺寸的值域都很大,但总长是固定的并且不太大。
此时我们可以将所有树状数组顺序放在一个数组上面,将询问离线后给每棵树状数组标一个起点和尺寸,每次从起点开始按照正常的二进制位做即可。
一般形式
先直接给出伪代码
inline void update(int *树状数组起点,int 树状数组尺寸,int 询问位置,int 更新值){
while(下标<=尺寸){
进行更新,
下标加上lowbit
}
}
inline void update(int *树状数组起点,int 树状数组尺寸,int 询问位置){
while(下标不为零){
统计贡献
下标减去lowbit
}
}
调用
update(树状数组+起点[i],尺寸[i],位置,值)
printf(query(树状数组+起点[i],尺寸[i],位置))
可以看到,和普通的树状数组几乎一样,因为拆分后的部分数组符合树状数组所有的性质。
例题
[P3960 列队](%3Ca href="https://www.luogu.org/problem/P3960"%3Ehttps://www.luogu.org/problem/P3960%3C/a%3E)
这道题抽象后需要维护\(N\)行中的\(Q\)个区间,需要构造\(N\)个数组支持二分查找每一个区间,这时候我们可以将区间按照行数编号排序,建一棵长度为\(Q\)的树状数组,然后用上面的方法对于每行建立树状数组,复杂的二分查找查找也可以进行。
inline void update(LL *ar,int siz,int x,LL c){
while(x<=siz){
ar[x]+=c;
x+=x&(-x);
}
}
inline int query(LL *ar,int siz,int x){
/*binary search*/
int l=1,r,mid,sum,ret;
while(l<=siz&&ar[l]<x){
l<<=1;
ret=l;
}
r=l;
sum=ar[(l>>=1)];
while(l<r-1){
mid=(l+r)>>1;
if(mid>siz||ar[mid]+sum>=x){
r=mid;
ret=mid;
}else{
l=mid;
sum+=ar[l];
}
}
ret=r;
return ret;
}