2681. 英雄的力量

难度困难

给你一个下标从 0 开始的整数数组 nums ,它表示英雄的能力值。如果我们选出一部分英雄,这组英雄的 力量 定义为:

  • i0i1 ,... ik 表示这组英雄在数组中的下标。那么这组英雄的力量为 max(nums[i0],nums[i1] ... nums[ik])2 * min(nums[i0],nums[i1] ... nums[ik])

请你返回所有可能的 非空 英雄组的 力量 之和。由于答案可能非常大,请你将结果对 10^9 + 7 取余。

示例 1:

输入:nums = [2,1,4]

输出:141

解释:

第 1 组:[2] 的力量为 22 * 2 = 8 。

第 2 组:[1] 的力量为 12 * 1 = 1 。

第 3 组:[4] 的力量为 42 * 4 = 64 。

第 4 组:[2,1] 的力量为 22 * 1 = 4 。

第 5 组:[2,4] 的力量为 42 * 2 = 32 。

第 6 组:[1,4] 的力量为 42 * 1 = 16 。

第 7 组:[2,1,4] 的力量为 42 * 1 = 16 。

所有英雄组的力量之和为 8 + 1 + 64 + 4 + 32 + 16 + 16 = 141 。

示例 2:

输入:nums = [1,1,1]

输出:7

解释:总共有 7 个英雄组,每一组的力量都是 1 。所以所有英雄组的力量之和为 7 。

提示:

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

# 贡献法

直接思路:要计算取出的最大值最小值,那就排好序后,依次枚举每个 22 个元素,对于其他中间元素,可选可不选,共有 2i2^i 种,这样是 O(N2)O(N^2)

对于 nums:[1,2,3,4,5]nums:[1,2,3,4,5]

此时最大值为 4444 作为一个数的力量为 434^3

  • 最小值为 11,最大值为 44,中间有 22 个元素:[2,3][2,3],其中每个元素选 / 不选,就是子序列,每一个都 22 种情况,即 2×2=222\times2=2^2 种情况
  • 最小值为 22,最大值为 44,中间有 11 个元素:[3][3],其中每个元素选 / 不选,就是子序列,每一个都 22 种情况,即 2=22=2 种情况
  • 最小值为 33,最大值为 44,中间有 00 个元素,只有 11 种情况。

归纳为:1×22×422×21×423×20×421\times 2^2\times4^2\\2\times 2^1\times4^2\\3\times 2^0\times4^2 +43+\,4^3

=42×(1×22+2×21+3×20)+43=42×v4+43=42(v4+4)= 4 ^2\times(1\times 2 ^ 2 + 2 \times 2^1 + 3\times2^0) +4^3= 4 ^2 \times v_4 + 4^3 = 4^2(v_4+4) (最小值的贡献 v4v_4

当枚举到 55 的时候,复用 v4v_4

归纳为:1×23×522×22×523×21×524×20×521\times 2^3\times5^2\\2\times 2^2\times5^2\\3\times 2^1\times5^2\\4\times2^0\times5^2 +53+5^3

=52×(v4×2+4×20)+53=52((v4×2+4)+5)= 5 ^2\times(v_4 \times 2 + 4 \times 2^0) + 5^3 = 5^2((v_4\times 2 + 4)+5) (贡献值)


假设有 f1f_1 f2f_2 ... fif_i fi+1f_{i+1} ... fnf_n, 求总数

对于fif_i, 遍历f1f_1 ~ fi1f_{i-1}, 求出贡献值 cic_i

对于fi+1f_{i+1}, 遍历f1f_1 ~ fif_i, 求出贡献值 ci+1c_{i+1}

此时 ansi+1=fi+12(ci+1+fi+1)ci+1=ci×2+fians_{i + 1} = f_{i+1} ^2 (c_{i+1}+f_{i+1}),c_{i + 1} = c_i \times 2 + f_i

class Solution {
    int MOD = 1000000007;
    public int sumOfPower(int[] nums) {
        Arrays.sort(nums);
        long ans = 0;
        // 初始化贡献值 c 等于 0,
        long c = 0;
        for (long num : nums) {
            ans = (ans + num * num % MOD * (c + num)) % MOD;
            c = (c * 2 + num) % MOD;
        }
        return (int) ans;
    }
}