固定总长的多棵树状数组
概述
有时候,我们需要很多棵树状数组,每棵尺寸的值域都很大,但总长是固定的并且不太大。
此时我们可以将所有树状数组顺序放在一个数组上面,将询问离线后给每棵树状数组标一个起点和尺寸,每次从起点开始按照正常的二进制位做即可。
一般形式
先直接给出伪代码
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 列队
这道题抽象后需要维护\(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; }