891. 子序列宽度之和

87 阅读2分钟

891. 子序列宽度之和

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

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

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

示例 1:

输入: 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:

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

提示:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

思路

题目要求的是所有子序列的最大值减去所有子序列的最小值

有多少个子序列? 2n2^n 个子序列

其中,除开空的子序列,一共 2n12^n - 1个子序列

nn变得很大时,我们不能够枚举去做

如何优化呢?

注意到,对于每一个数字,最大值和最小值分别在什么样的序列里生效呢?

这里举最小值为例:

对于序列 x1,x2,x3,...,xnx_1, x_2, x_3, ..., x_n来说,设 xkx_k为最小值,假设有mm个值是大于等于xkx_k

则我们的xkx_k的值可以被累加到这一些上面

那么,这些数,一共有多少个呢?

利用排列组合容易得到:Cm0+Cm1+Cm2+...+CmmC_m^0 + C_m^1 + C_m^2 + ... + C_m^m => i=0mCmi\sum_{i=0}^mC_m^i => 2n2^n

一共有这么多种排列

所以,只需要把这些排列总数计算出来然后再乘以xkx_k即可

最大值一样的道理

2n2^n的计算会溢出

这里因为会用到202^0~2n12^{n-1}所以数,所以你可以预处理出这些值,或者采用快速幂来做这一件事情

总体时间复杂度 O(nlogn)O(nlogn)

代码

class Solution {
public:
    int sumSubseqWidths(vector<int>& ve) {
        const int MOD = 1e9 + 7;
        sort(begin(ve), end(ve));
        auto qmi = [&](int n, int base) -> long long {
            int res = 1;
            while (base) {
                if (base & 1) res = (1ll * res * n) % MOD;
                n = (1ll * n * n) % MOD;
                base >>= 1;
            }
            return res;
        };
        long long ans = 0;
        for (int i = 0; i < ve.size(); i ++) {
            ans = ((ans - qmi(2, ve.size() - 1 - i) * ve[i]) % MOD + MOD) % MOD;   
            ans = (ans + qmi(2, i) * ve[i]) % MOD;
        }
        return ans;
    }
};