分块 学习笔记
分块是一种思想,指的是把数据划分为若干块,然后将块作为整体处理。此指数据结构中的块状数组。下文以维护区间加,区间查询为例。
初始化
一般来说,如果给出的数组长度为 \(n\),块数会取 \(\sqrt{n}\) 使块长与块数均衡。然后预处理块端点,每一个下标属于的块,块长与要维护的信息。末尾剩下的合并到最后一块。
void build(int n){
bn=sqrt(n);
for(int i=1;i<=bn;i++)bl[i]=n/bn*(i-1)+1,br[i]=n/bn*i;
br[bn]=n;
for(int i=1;i<=bn;i++)len[i]=br[i]-bl[i]+1;
for(int i=1;i<=bn;i++)for(int j=bl[i];j<=br[i];j++)bp[j]=i,v[i]+=a[j];
}
区间加
当加的区间左右端点在同一个块时,直接修改原数组和每一块的和。
否则,先处理左右不完整的块,然后对中间完整的块打标记。
void add(int l,int r,long long x){
if(bp[l]==bp[r])for(int i=l;i<=r;i++)a[i]+=x,v[bp[i]]+=x;
else{
for(int i=l;i<=br[bp[l]];i++)a[i]+=x,v[bp[i]]+=x;
for(int i=bp[l]+1;i<bp[r];i++)tag[i]+=x;
for(int i=bl[bp[r]];i<=r;i++)a[i]+=x,v[bp[i]]+=x;
}
}
区间查询
同理,区间左右端点在同一个块时,直接统计答案,否则,先统计左右不完整的块,然后计算中间完整的块。
long long query(int l,int r,long long ans=0){
if(bp[l]==bp[r])for(int i=l;i<=r;i++)ans+=(a[i]+tag[bp[i]]);
else{
for(int i=l;i<=br[bp[l]];i++)ans+=(a[i]+tag[bp[i]]);
for(int i=bp[l]+1;i<bp[r];i++)ans+=(v[i]+tag[i]*len[i]);
for(int i=bl[bp[r]];i<=r;i++)ans+=(a[i]+tag[bp[i]]);
}
return ans;
}
复杂度证明
假设 \(n,m\) 同阶。每次执行操作,左右的散块是根号级别的,中间完整的块也不超过根号级别。因此时间复杂度 \(O(n\sqrt{n})\),是根号算法。虽然复杂度逊于对数级算法,但是灵活性更高,常数不大。
一般情况下块数为 \(O(\sqrt{n})\),但有时可以微调块长。但是这就比较玄学了。
[[数据结构]]

浙公网安备 33010602011771号