参考自:黄学长的分块入门 推荐!
信息学中的分块思想.pdf(百度就能百度到文件)这个有些错误…
分块的思想
简述
将规模为n的问题,分成 n−−√ 块,每块规模也是 n−−√ ,那么对块内的操作和整个范围的操作的复杂度平均,可以在较优的空间复杂度下,将每次操作的复杂度降低到\sqrt{n} ,以较低的编码 复杂度解决问题。
入门1
我们先来思考:原序列元素<—>所在块的对应关系
initially, 我们分好了块。
对于原序列元素a[ pos ],在哪个块:
pos−1n√+1
;
so,我们可以维护哪些信息?
原序列的每个元素 and 块 and …
题目:给出一个长为n的数列,以及n个操作,操作涉及区间法,单点查值。
分块思想:
先把每个元素分块包装,就是分块(堆),拿一个属性,去代表一段区间的属性。
这里有两个“加法”标记:块的/单个元素;
当 “区间[Left, Right]加 + val” 的时候,
1. 如果Left, Right 在同一个块,直接暴力维护单点标记,复杂度O(
n−−√
);
2. 不在同一个块,
对于区间[Left,Left所在块的右端点]&&[Right所在块的左端点]暴力维护单点标记,复杂度O(
n−−√
);
对于区间[Left所在块+1,Right所在块-1]这些块,进行维护块的加法标记,复杂度O(
n−−√
);
对于 m 次询问,总的复杂度:m
n−−√
.
代码注意:
块区间大小:s =
n−−√
;
原序列元素a[ pos ]的块位置:belong[ pos ] =
pos−1s+1
;
原序列元素a[ pos ]的块的左端点:(belong[ pos ] - 1) * s + 1;
原序列元素a[ pos ]的块的右端点:belong[ pos ] * s;
代码具体实现:
PS: 这里单个元素维护数组直取代了原序元素数组;
while(m--) //操作数
{
scanf("%d",&op);
if(op) //区间加
{
scanf("%d%d%d",&Left,&Right,&val);
//维护Left所在块
for(int i=Left; i<=min(bel[Left]*s,Right); i++) v[i]+=val;
//维护Right所在块
if(bel[Left] != bel[Right])
{
for(int i=(bel[Right]-1)*s+1; i<=Right; i++) v[i]+=val;
}
//维护块
for(int i=bel[Left]+1; i<=bel[Right]-1; i++) vtag[i]+=val;
}
else //查询
{
scanf("%d",&pos);
printf("%d\n",v[pos]+vtag[bel[pos]]);
}
}