多段树状数组

固定总长的多棵树状数组

概述

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

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

一般形式

先直接给出伪代码

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;
}
posted @ 2019-08-07 15:29  guoshaoyang  阅读(204)  评论(0编辑  收藏  举报