LC-891.子序列宽度之和

题目描述

leetcode 困难题

一个序列的 宽度 定义为该序列中最大元素和最小元素的差值

给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和 。由于答案可能非常大,请返回对 1e9 + 7 取余 后的结果。

子序列 定义为从一个数组里删除一些(或者不删除)元素,但不改变剩下元素的顺序得到的数组。例如,[3,6,2,7] 就是数组 [0,3,1,6,2,2,7] 的一个子序列。

示例1:

1
2
3
4
5
输入:nums = [2,1,3]
输出:6
解释:子序列为 [1], [2], [3], [2,1], [2,3], [1,3], [2,1,3]
相应的宽度是 0, 0, 0, 1, 1, 2, 2 。
宽度之和是 6 。

示例2:

1
2
输入:nums = [2]
输出:0

提示1:

1
2
1 <= nums.length <= 105
1 <= nums[i] <= 105

考虑每个元素的贡献

考虑到数据范围,肯定是无法通过求具体的子序列来得到答案的。

我们可以考虑每个元素对答案的贡献来得到最终的答案。并且由于宽度为序列中最大元素和最小元素的差值,那么也就是说每个元素只有在作为某个序列的最小值或者最大值时,才会在该序列中对答案有贡献。容易想到先对数组进行排序,那么排序后就能可以得到以某个 $nums[i]$ 为最大值的序列一共有 $2^i$ 个,因为左边的每个元素都可以选或者不选,即一共会有 $2 \times 2 \times 2 … \times 2$ 个;同理右边就有 $2 ^{j-i-1}$ 个以 $num[i]$ 作为最小值的序列。

需要注意的是,根据题意,长度为 1 的序列是没有贡献的,所以 $nums[i]$ 的左边不能全部不选,右边也不能全部不选,也就是说,以 $nums[i]$ 作为最大值或最小值的序列数量均需要分别减一。

那么就可以得到,每个元素对答案的贡献为 $nums[i] \times (2^i - 1 - (2^{j-i-1} - 1) )$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
private static final int MOD = (int)(1e9 + 7);

public int sumSubseqWidths(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
long[] pow = new long[n + 1];
pow[0] = 1;
for(int i = 1; i < n + 1; i++){
pow[i] = pow[i - 1] * 2 % MOD;
}
long ans = 0;
for(int i = 0; i < n; i++){
ans = (ans + (nums[i] * (pow[i] - 1 - pow[n - i - 1] + 1)))% MOD;
}
return (int)(ans % MOD);
}
}

LC-891.子序列宽度之和
https://wecgwm.github.io/2022/11/18/LC-891-子序列宽度之和/
作者
yichen
发布于
2022年11月18日
许可协议