题目分析与解法思路
给定一个长度为 n 的数组 a,我们需要计算所有子数组的权值和,并且结果对 109+7109+7 取模。数组的权值定义为:
权值(b)=1×b1+2×b2+⋯+m×bm权值(b)=1×b1+2×b2+⋯+m×bm
对于所有子数组的权值和,要求的是对所有可能的子数组的权值进行求和。
基本思路
首先要明确子数组的定义:一个子数组是原数组的连续部分。比如,对于数组 a = [1, 2, 3],其所有的子数组包括:
[1][2][3][1, 2][2, 3][1, 2, 3]
每个子数组的权值计算规则是:
- 子数组
[b_1, b_2, ..., b_m]的权值为 1×b1+2×b2+⋯+m×bm1×b1+2×b2+⋯+m×bm,即数组中每个元素按其索引加权。
具体思路
-
子数组数量: 对于一个长度为
n的数组a,它总共有 n×(n+1)22n×(n+1) 个子数组。对于每个子数组,我们都要计算它的权值。 -
优化权值的计算: 直接遍历所有子数组并计算权值虽然是一个可行的方法,但时间复杂度为 O(n3)O(n3),会在数据较大时超时。为了优化,考虑从每个元素对子数组权值的贡献。
- 每个元素
a[i]会出现在多个子数组中,且出现在不同的权值位置。具体来说,a[i]会出现在从i到n的所有子数组中,并且在这些子数组中的位置不同。通过数学归纳和总结,可以得出每个元素的贡献。
- 每个元素
-
贡献计算: 对于数组中的每个元素
a[i],它出现在子数组中的位置是从i到n的所有子数组中,具体计算方法如下:- 元素
a[i]会在下标j从i到n的子数组中出现,其中j表示子数组的结束位置。 - 对于固定的
i,a[i]的总贡献为 a[i]×(i+1)×(n−i)a[i]×(i+1)×(n−i),其中(i + 1)是它在所有子数组中权重的累加系数,(n - i)是它出现在多少个子数组中的频率。
- 元素
-
取模操作: 为了避免结果溢出,所有的计算都应对 109+7109+7 取模。
优化后的计算过程
我们可以用以下公式计算所有子数组的权值和:
总权值和=∑i=1na[i]×(i+1)×(n−i+1)总权值和=i=1∑na[i]×(i+1)×(n−i+1)
其中:
- a[i]a[i] 是数组中的元素;
- (i+1)(i+1) 是它在子数组中的权重;
- (n−i+1)(n−i+1) 是它参与的子数组数量。
代码实现
pythonCopy Code
MOD = 10**9 + 7
def sum_of_subarray_weights(n, a):
total_sum = 0
for i in range(n):
# 计算当前元素的贡献
# (i + 1) 是权重位置的加权,(n - i) 是该元素参与的子数组数量
total_sum += a[i] * (i + 1) * (n - i) # 用 1-based 索引来计算
total_sum %= MOD # 对每次累加的结果取模
return total_sum
# 测试样例
print(sum_of_subarray_weights(3, [1, 2, 3])) # 输出: 33
print(sum_of_subarray_weights(4, [4, 5, 6, 7])) # 输出: 203
print(sum_of_subarray_weights(2, [10, 20])) # 输出: 80
代码解析
-
MOD常量:为了处理大数,所有的运算都需要对 109+7109+7 取模。
-
主逻辑:
- 我们遍历数组中的每个元素
a[i]。 - 计算每个元素在所有子数组中的贡献值:
a[i] * (i + 1) * (n - i)。 - 每次加到总和后都取模 109+7109+7,以防止溢出。
- 我们遍历数组中的每个元素
-
时间复杂度:
- 对于每个元素,我们进行常数时间的操作,时间复杂度为 O(n)O(n)。
思考与优化
- 通过观察,我们将子数组的计算转化为每个元素的贡献计算,避免了重复计算每个子数组的权值。
- 这种方法大大降低了复杂度,从暴力的 O(n3)O(n3) 降到了 O(n)O(n),对于大数据量的情况能快速计算出结果。
总结
这道题目考察了如何高效地计算所有子数组的权值和。通过将问题转化为每个元素对所有子数组的贡献,我们有效地避免了重复计算,降低了时间复杂度。此方法不仅解决了问题,还通过取模确保了计算结果不会溢出。