引言:
对于区间问题,暴力算法(一个点一个点跳)的复杂度太高,尝试优化。可以将一些点“捆绑起来”,记录一下整体的贡献/维护信息,这样跳的时候就可以一块块跳,复杂度有很大优化。
总述:
对于一个长度为n的序列,我们可以按照一个固定的长度m,顺序将序列分成n/m+(n%m>0?1:0) 组。对于每组,我们可以进行一点处理,维护出该组的整体贡献。这样询问到包含这组的区间时,我们可以直接返回该组的整体贡献信息而非暴力查询该组的每一点;修改包含这组的区间时,可以通过打标记的方式,减少复杂度。
分析时间复杂度(块的数量与n/m同规模,为了方便,下文将n/m看做与块的数量 相等):
预处理:O(n)分块,n/m个块,若给每个块做O(x)的处理,知总复杂度:
O(n+n/m *x)
区间操作:
若该组的整体贡献可以通过维护信息O(k)得出,对于区间完整包含的块,最多有n/m个块,对每个块O(k)求出整体贡献。对区间不完整包含的块(区间左端、右端),最多有2块,2m个点,暴力枚举单点处理,设枚举一个点进行处理的复杂度为O(y)。知一次操作的复杂度:
O(n/m*k+ m*y)
综上,设有w个操作,则总复杂度:O(n+n/m*x+w*(n/m*k+ m*y))。一般取x=w=n,k=y=1,则总复杂度相当于O(n(n/m+m)) 由均值不等式知取m=sqrt(n)时复杂度最小,为O(nsqrt(n))。故块的大小常常取sqrt(n)。
(开o2才能满分的正解。。。)
题解参考大佬博客
总结:
当问题可以分解(或说可由小区间合并得到答案)时,可用线段树或分块做。
但线段树的树形结构有时候会约束它的使用,例如单点修改时要额外修改该点到根路径上的所有点,导致在线段树节点中能乱搞的力度不如分块大。
(树套树:“论单点乱搞力度谁有我大?“
(捂嘴)
分块:“闭嘴吧你,可不看看你那代码不下400行都写不完”)
分块虽然时间复杂度略逊一筹(n=1e5的情况下也慢不了多少),但块内乱搞的灵活性高(例题就是给每块内都排序,线段树做得到吗),双方各有千秋吧。