Description
给定 n 个数 ai,m 个询问,求区间 [l,r] 中的最小众数,强制在线。
(1≤n≤4×104,1≤m≤5×104,1≤ai≤109)
Source
Solution 1
考虑 分块 。我们可以预处理出 fi,j 为第 i 个块到第 j 个块的区间最小众数,那样区间 [l,r] 的答案只有可能是 中间所有块的区间最小众数,左边多余块中的数 或 右边多余块中的数(如图)。
很容易在 O(块的大小×n) 的时间内预处理出所有的 fi,j,但是怎么求出左右多余块中的数在区间 [l,r] 中的出现次数呢?我们可以用 vector 存下每一个 ai 在序列中的所有出现位置(要先离散化)。在询问时,对于左右多余块中的每一个数 x,我们可以二分找到端点 l,r 在 x 对应的 vector 中的位置(可以用 STL 中的 lower_bound 和 upper_bound),相减即是 x 在区间 [l,r] 中的出现次数。注意如果该数的出现次数与当前答案的出现次数相同,但这个数更小,也要更新答案,因为要求的是最小众数。
设块的大小为 S,则总时间复杂度为 预处理 O(n2S) + 查询 O(mSlogn),根据 均值不等式,可知取 S=n√mlogn 时总时间复杂度最小,为 O(n√mlogn) 。
Code 1
1 |
|
Solution 2
考虑怎么优化上述做法(即减少查询的时间复杂度)。
很容易发现查询时答案在区间 [l,r] 中的出现次数其实是单调不减的。
我们可以预处理出 posi,表示 i 的位置在 ai 的 vector 中对应第几个数。假设当前我们已经知道当前的最小众数是 mode,且 mode 的出现次数为 maxn 。左边和右边多余的块分类讨论:
对于左边多余块中的某个数 x,x 在其对应 vector 中的位置是 posx 。我们只需要判断 x 出现的次数是否大于或等于 maxn 即可,因为如果这个数出现的次数小于 maxn,就不可能更新答案。如果 x 第 posx+maxn 次出现的位置 ≤r,就说明区间 [l,r] 中 x 的出现次数为 maxn+1,比当前答案更优。如果 x 第 posx+maxn−1 次出现的位置 ≤r,且 x 比答案小,就说明 x 也能更新答案。
右边多余的块同理,只是变成了判断 x 第 posx−maxn 次出现的位置是否 ≥l 。
写代码时有很多细节需要注意。比如要先判断 vector 的下标是否小于 0 或者超过 size,然后再访问。
很显然这种做法在查询时不需要二分,时间复杂度少一只 log 。设块的大小为 S,则总时间复杂度为 预处理 O(n2S) + 查询 O(mS),根据 均值不等式,可知取 S=n√m 时总时间复杂度最小,为 O(n√m) 。
Code 2
1 |
|
Be the first person to leave a comment!