分块算法入门
所谓分块算法,就是讲一个序列分成若干块,维护块内的信息。为了保证一定的时间复杂度,所以对于一个 个元素组成的数组,将其分为 块,每块也有 个元素。所以一般分块算法的复杂中都带有根号。
对于一个暴力的区间修改问题,方法自然是对于区间的每一个元素都暴力修改,这样的话时间复杂度是明显不够优秀的。所以引入了分块的算法,分块相当于一种优雅的暴力。
如何写分块(一)
维护内容
在分块中,我们需要维护的基本内容有:总的元素个数 ,每一块的个数 ,块的总数目 ,这块左端点的位置 ,这块右端点的位置 ,元素 具体属于那一块,根据具体情况,可以适当修改。
下面就对上面需要进行预处理的一一解释。
每一块的个数
这个就很简单,根据分块的思想,每一块要维护 个元素,故每一块的个数 。
块的总数目
总共分为 个块,那么每一块就有 个元素,这时候要注意是否能整除,若可以则恰好分完,否则 。
块的左右端点位置
如果我们求出了每一块的左右端点,那么也就相当于计算出了每一块所维护信息的左右区间,由于每一块有 个元素,我们这里设第 块所维护的区间为 ,则有如下的式子:
这样便可以求解出每块所维护的区间。
每个元素所属于的块号
自然我们想到用元素下标 去除以每块的个数 ,这样的话,当 的时候显然是不正确的,故稍作修正 即可。
完整代码
int n,m,num,block;
int l[nmax],r[nmax],belong[nmax];
void build(){
block = sqrt(n);
num = n / block; if(n % block) num++;
for(int i = 1;i<=num;++i)
l[i] = (i-1) * block + 1,
r[i] = i * block;
r[num] = n;
for(int i = 1;i<=n;++i) belong[i] = (i-1) / block + 1;
}
如何写分块(二)
思维的转换
上面的一些基本的内容,只是为了我们更快的维护其他的数据,以达到题目的要求,如果有线段树的基础,你可以把分块理解为既加强又退化的线段树。
为什么这么说呢?
在我个人看来,线段树相当于对原数组不断进行分块,直到最后不可再分。由于一块分2块,2块分4块,这样的性质保证了复杂度在 的数量级上。而分块只分一次,每块 个元素。
由于线段树所分块太多,所以在面对不符合区间加法的时候,就会显得十分无力。对于分块,可以采用部分暴力部分利用块内信息来统计。
由于我目前所做分块题目比较少,以上只是一些个人的看法,也希望大家可以留言交流。
如何训练
这里推荐黄学长的 「分块」数列分块入门1 – 9 by hzwer,做题地址在LOJ。
黄雪长的博客有详细的解法和代码。
To be continue.