LC 1862. 向下取整数对和
https://leetcode-cn.com/problems/sum-of-floored-pairs/
这题很有意思的点在于,floor/ceil这类函数是没有办法通常乘法来做到减少计算的。也就是说 floor(X/Y) = floor(X/Z) * floor(Z/Y). 我之前还考虑过是否要保留X/Y的原始累计值,但是发现这个思路也是错误的。
打开下面两个提示,就会有思路:
- Find the frequency (number of occurrences) of all elements in the array. # 这个不算是思路,因为有重复元素,所以频数肯定是要做统计的
- For each element, iterate through its multiples and multiply frequencies to find the answer. # 这个提示很有用,可以变为寻找x的乘数。
也就是说,我们要换个思路。如果数组中存在某个X,我们可以去寻找
- [X, 2X) 所有数的出现次数A, 那么这些数的floor(arr[i]/X) = 1, 所以总数就是A
- [2X,3X) 所有数的出现次数B,那么这些数的floor(arr[i]/X) = 2, 所以总数就是2*B
- 以此类推,直到超过数组最大数为止。
关于时间复杂度不太好计算,可能最坏的情况就是没有重复的数组[1..10^5]了。这个情况下面需要进行:10^5(1+1/2+1/3+…) 后面部分是个调和级数 https://zh.wikipedia.org/wiki/%E8%B0%83%E5%92%8C%E7%BA%A7%E6%95%B0. 这个值大约是12.09. 所以应该是不会超时的。
In [1]: s = 0 In [2]: for i in range(1, 10**5+1): ...: s += 1/i In [3]: s Out[3]: 12.090146129863335
代码如下,计算出现次数的话可以使用前缀和。
class Solution: def sumOfFlooredPairs(self, nums: List[int]) -> int: N = min(10 ** 5, max(nums)) occ = [0] * (1 + N) for x in nums: occ[x] += 1 acc = [0] * (2 + N) for i in range(1, N + 1): acc[i + 1] = occ[i] acc[i + 1] += acc[i] ans = 0 for x in range(1, 1 + N): if occ[x] == 0: continue m = 1 while True: t = m * x if t > N: break s, e = t, min(t + x - 1, N) tt = acc[e + 1] - acc[s] # if tt: # print(x, m, s, e, tt) ans += tt * occ[x] * m m += 1 MOD = 10 ** 9 + 7 return ans % MOD