多段树状数组

固定总长的多棵树状数组

概述

有时候,我们需要很多棵树状数组,每棵尺寸的值域都很大,但总长是固定的并且不太大。

此时我们可以将所有树状数组顺序放在一个数组上面,将询问离线后给每棵树状数组标一个起点和尺寸,每次从起点开始按照正常的二进制位做即可。

一般形式

先直接给出伪代码

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;
}

猜你喜欢

转载自www.cnblogs.com/guoshaoyang/p/11315592.html