在“忠诚”这题中,我使用了乱搞解法,因为数据水通过了。
做法如下:
- 将 A 数组从大到小排序,并且记录每个数原来的下标。
- 查询时,将 A 数组从头到尾遍历一遍,找到第一个原来的下标在询问区间内的数,并输出它。
这么猎奇的做法为什么能过呢?原来它在随机数据(Ai,查询区间的左、右端点均随机生成)下,时间复杂度期望 O(qlogn)!接下来,我要证明这个结论:
由于随机生成,所以排序后原来的下标也是随机的。这就相当于在数组中随机撒点,看看哪个点最先落在区间上。显然,如果长度为 l,每次撒点撒中的概率为 nl,而有点撒中的期望撒点次数为 ln。
那 l 为多少呢?显然它由区间的左右端点决定。注意到区间左右端点是随机的,所以 l=1 的概率约为 n2n,l=2 的概率为 2×n2n−1,l=3 的概率为 2×n2n−3,......,l=n 的概率为 2×n21。
所以,整体的期望如下:
n2n×1n+2(n2n−1×2n+n2n−2×3n+...+n21×nn) <2(nn+1+2nn+3nn−1+...+n22) <2(nn+1+2nn+1+3nn+1+...+n2n+1) =2(nn+1)(11+21+31+...+n1) <2(nn+1)(11+21+21+41+41+41+41+...+2⌊log2n⌋1+2⌊log2n⌋1+...+2⌊log2n⌋1) =2(nn+1)(⌊log2n⌋+1) =O(logn)
当时概率算错了导致式子升了一级,懒得改了直接在第二步写个小于号得了
所以时间复杂度为 O(qlogn)。
hack 这个算法的做法也挺简单的,一种做法如下:
将第 i 次询问的区间设置成 [i,i] 即可。
显然无论 A 数组长什么样,这些查询会让该算法分别撒点 1 次,2 次,3 次,......,n 次,时间复杂度退化为 O(qn)。
有帮助,赞一个