本周小结
这周复习了平衡树的内容,学习了根号分治
的算法思想。
根号分治听起来比较冷门,但是是一种很好用也很精妙的思想。
根号分治
一道题目:哈希冲突
暴力
如果使用暴力,每次询问扫一遍,对于一次查询的时间复杂度为\(O(\frac{n}{x})\)。当\(x=1\)时是最差时间复杂度,也就是说,最坏情况下,总体时间复杂度是\(O(mn)\)。
但是,如果是随机的呢?
我们知道调和级数\(\sum_{i=1}^{n}\frac{n}{i} \approx n\log n\),所以随机数据的单点问询时间复杂度为\(O(\log n)\)!在随机数据下,暴力的时间复杂度不会很差!
从询问入手
这也是我感到新奇的地方,这种思想的精妙之处也体现在这里。
我们发现:对于\(x\)较大的情况,即使是暴力去做也是很快的。主要就是思考对于\(x\)较小时如何去做。(\(x\)较大和较小的定义后文会说)
于是现在暴力处理小\(x\)时的缺陷需要弥补,其实对于较小的\(x\)而言,我们可以将当前序列拆成\(x\)个小序列。具体来说,是定义一个二维数组\(ans[i][j]\),表示当前模\(i\)的\(j\)池子的元素总和。
所以,我们现在假设一个数\(L\)。当\(x\leq L\)时叫做\(x\)较小,当\(x>L\)是叫做\(x\)较大。对于较小的\(x\),需要定义数组int ans[SIZ][SIZ];
。一开始的预处理是\(O(nL)\)的时间,每加入一个元素是\(O(L)\)的时间,查询较小\(x\)的时间复杂度是\(O(1)\),较大\(x\)的时间复杂度是O(\frac{n}{L})$
时间复杂度分析
接下来分析\(L\)的取值,以及总体的时间复杂度
分析最坏情况下的时间复杂度,假设有\(uC\)个查询,\(uA\)个加入总时间为\(O(nL+uA\times L+uC\times \frac{n}{L})\)
即\(O(L(n+uA)+uC\times \frac{n}{L})\)
根据均值不等式,得出时间复杂度最小是\(L(n+uA)+uC\times \frac{n}{L}>=2\sqrt{(n+uA)*uC*n}\approx O(\sqrt{(n+uA)*uC*n})\)
根据均值不等式取最小值的条件,\(L(n+uA)=uC\times \frac{n}{L}\)
所以\(L=\sqrt{\frac{uC*n}{n+uA}}\)
如果觉得统计\(uC\)和\(uA\)麻烦的话,可以近似的取\(uA=uC=\frac{m}{2}\)
最终,\(L=\sqrt{\frac{mn}{2n+m}}\),时间复杂度为\(O(\sqrt{n^2m+nm^2})\)
注意出错的点
1、\(L\)求出来后最好与\(SIZ\)比较大小,避免ans
数组越界
2、虽然是分块,但\(L\)的取值并不是简单的\(\sqrt n\)
其他题目
SHOI2006homework 一种很好的根号分治,\(L\)取值较为麻烦,可以将询问一并读入后直接取\(L=\sqrt{R}\)(\(R\)为最大的数)
想法
根号分治就是将询问分为两部分,而时间复杂度是与这种询问有关的。需要取得一个平衡,那么就要用两种不同的方法来解决题目。一种是善于处理小数据的,一种是善于处理大数据的。如此可以使得时间复杂度成为平衡,时间复杂度的计算以及\(L\)值的选取是很重要的一个难点。