浅谈根号分治
一、问题引入
给定一个长度为\(n\)的序列,进行\(m\)次询问。每次询问给出两个数字\(x,y\)。对于每次询问,输出所有下标模除\(x\)等于\(y\)的元素的总和。
对于这个问题,我们发现他要维护的是一段离散的元素的和,而我们平时学的数据结构,如线段树等都只能维护一段连续的区间的和,那这个问题要如何进行处理呢?
仔细观察这个问题,我们可以发现这样两种方法:
- 我们可以用一个数组 \(sum[x][y]\) 来预处理出所有的下标模除\(x\)等于\(y\)的元素的和,然后进行\(O(1)\)的查询。但这个方法存在的问题为,只能处理x较小的情况。当x过大时,时间开销和空间开销都会变得很大
- 我们可以直接暴力求解查询,对询问
x y
,写一个从y开始到n结束的循环,每次增长的步长为x。这个方法存在的问题为,只能处理x较大的情况。当x过小时,单次询问的复杂度会趋近于\(O(n)\)
我们刚刚提到了两种方法,一种只能处理x较小的情况,而另一种只能处理x较大的情况。那么我们是否可以综合这两种方法。当x较小时使用第一种方法,而当x较大时使用第二种方法呢?答案是显然的,这就是我们今天要讲的内容——根号分治
首先我们考虑以k作为分界点,当x小于k时使用第一种方法进行预处理。当x大于k时使用第二种方法进行暴力查询。首先来分析复杂度
如果我们要预处理所有小于k的情况,那么预处理的复杂度即为\(O(nk)\),单次询问的复杂度为\(O(1)\),所以第一种方法的复杂度即为\(O(nk)\)。
下面考虑第二种方法的复杂度,由于循环的步长为x,所以单次查询的复杂度即为\(O(\frac{n}{k})\),最多可以进行m次询问,所以总的复杂度最坏为\(O(\frac{nm}{k})\),在这个问题中\(n,m\)同阶,所以上述复杂度还可以写成\(O(\frac{n^2}{k})\)
由于我们要综合使用两种方法,所以总的复杂度便是\(max(nk,\frac{n^2}{k})\)。注意到当\(k = \sqrt n\)时该式有最小值 \(n \sqrt n\)所以如果我们将\(k = \sqrt n\)作为第一种方法和第二种方法的分界点。最后的复杂度便是\(O(n \sqrt n)\)