Luogu5611 Ynoi2013 D2T2/牛客挑战赛32F 最大子段和 分块、分治

传送门


之前一直咕着的,因为一些特殊的原因把这道题更掉算了……

有一个对值域莫队+线段树的做法,复杂度\(O(n\sqrt{n} \log n)\)然而牛客机子实在太慢了没有希望(Luogu上精细实现似乎可以过)。

考虑对序列进行块大小为\(B=\sqrt{n}\)的分块。对于某一个块来说,如果我们要对这个整块进行询问,那么一次询问一定会保留这\(B\)个数按照值域排序之后的一段区间,其余都变成\(0\)。也就是说本质不同的询问只有\(O(B^2)\)种。

如果可以对这\(O(B^2)\)种询问处理出每一种对应询问下这一块中的最大子段和、最大前缀和、最大后缀和和总和,那么对于一组询问就只需要把整块的信息拿过来把散块暴力合并就可以了。

注意到计算最大子段和具有结合律,我们考虑分治。当我们在解决一段区间\((l,r)\)时候,先从中间劈成\((l,mid)\)\((mid+1,r)\)两半递归下去做,递归边界是\(l=r\)。接下来我们考虑当我们解决了\((l,mid)\)\((mid+1,r)\)的问题时如何合并。

首先我们可以使用归并得到当前区间按照值域排序后的数组,然后我们需要通过左右两边得到的答案来得到左右端点分别取在其中某个位置的时候的答案。假设需要求值域左右端点为\((l,r)\)的答案,那么在左区间中找到最小的\(\geq l\)的值\(l_1\)和最大的\(\leq r\)的值\(r_1\),那么对于左区间来说询问\((l,r)\)等价于询问\((l_1,r_1)\),而后者在之前处理的答案里面存在。所以我们找到\(l_1,r_1\)就可以得到左区间的信息,右区间同理。我们可以通过这个方式来得到当前区间下的所有答案。

使用单调性将每一层合并的复杂度降低为区间长度的平方,那么分治的复杂度可以由\(T(n) = 2T(\frac{n}{2}) + O(n^2)\)计算,根据主定理\(T(n) = O(n^2)\)。也就是我们将所有块的答案预处理出来的复杂度是\(O(n \sqrt{n})\)的。

然后我们只需要对于每一次询问找到每一块内这组询问的值域区间对应在块上的哪一个区间。在线做需要二分,所以可以把询问离线下来,把所有需要求位置的所有值域位置先排序然后双指针求出对应位置。复杂度\(O(n \sqrt{n})\)

题外话:这东西常数贼大,尽量避免使用STL是卡常的很好的选择……

Code

posted @ 2019-12-18 21:32  cjoier_Itst  阅读(...)  评论(...编辑  收藏