题目链接
英文链接:https://leetcode.com/problems/sum-of-subsequence-widths/
中文链接:https://leetcode-cn.com/problems/sum-of-subsequence-widths/
题目详述
给定一个整数数组 A ,考虑 A 的所有非空子序列。
对于任意序列 S ,设 S 的宽度是 S 的最大元素和最小元素的差。
返回 A 的所有子序列的宽度之和。
由于答案可能非常大,请返回答案模 10^9+7。
示例:
1 | 输入:[2,1,3] |
提示:
- 1 <= A.length <= 20000
- 1 <= A[i] <= 20000
题目详解
因为对于每一个非空子序列,只用关注其中的最大元素和最小元素,与序列的顺序无关,所以我们可以先对数组进行排序,方便处理。
用
(i, j)
表示固定两端的序列,它的最小值为a[i]
,最大值为a[j]
,这样的序列有2 ^ j - i - 1
个。每个序列的宽度为a[j] - a[i]
,这一组序列的总宽度为(a[j] - a[i]) * (2 ^ j - i - 1)
。此时可以在O(n^2)
的时间复杂度内解决问题。但是数据范围达到了 20000,O(n^2)
的算法不可取。要想办法降低时间复杂度。对于以
a[j]
结尾的序列,它的总宽度为:result = (a[j] - a[0]) * (2 ^ j - 1) + (a[j] - a[1]) * (2 ^ j - 2) + ... + (a[j] - a[j - 1]) * (2 ^ 0)
。可表示为
result = X - Y
,其中X = a[j] * (2 ^ j - 1 + 2 ^ j - 2 + ... + 2 ^ 0) = a[j] * ((2 ^ j) - 1)
。Y = a[0] * 2 ^ j - 1 + a[1] * 2 ^ j - 2 + ... + a[j - 1] * 2 ^ 0
。X 可以在常量时间内得到,Y 不行,但是可以知道
Y[j + 1] = Y[j] * 2 + a[j]
。有了这个递推表达式,每个 Y 也可以在遍历过程中常数时间得到。在计算过程中 res 可能为负,为了保证得到正确的结果,最后进行一次
(res + MOD) % MOD
这个操作,保证结果为正。算法时间复杂度为
O(nlogn)
,主要花费在排序上,排序完成后只需要O(n)
的扫描。
1 | public class LeetCode_00891 { |
可以换一种思路,直接考虑以某个位置为端点的情况。
同样先对数组进行排序,方便处理。
在一个从小到大排序的数组中,有
i
个数小于A[i]
,有n - 1 - i
个数大于A[i]
。那么以
A[i]
为最大值的非空子序列有2 ^ i
个。那么以
A[i]
为最小值的非空子序列有2 ^ n - 1 - i
个。那么对于每一个
A[i]
,它的贡献为A[i] * (2 ^ i) - A[i] * (2 ^ n - 1 - i)
。因为求的是最大元素和最小元素的差,作为最大值会采用加法,作为最小值会采用减法。最终的结果就是
sum{A[i] * (2 ^ i) - A[i] * (2 ^ n - 1 - i)}, 0 <= i < n
=
(A[0] * (2 ^ 0)) - (A[1] * (2 ^ 1)) + (A[i] * (2 ^ i)) - A[i] * (2 ^ n - 1 - i) + (A[n - 1 - i] * (2 ^ n - 1 - i)) - A[n - 1 - i] * (2 ^ i) + (A[n - 1] * (2 ^ n - 1)) - (A[n - 1] * (2 ^ 0))
=
sum{A[i] * (2 ^ i) - A[n - 1 - i] * (2 ^ i)}, 0 <= i < n
。通过一次遍历,即可求出结果。
算法时间复杂度为
O(nlogn)
,主要花费在排序上,排序完成后只需要O(n)
的扫描。
1 | public class LeetCode_00891 { |