一、题目
题目描述
小基拿到了一个数组,他每次操作会将除了第 xxx 个元素的其余元素翻倍,一共操作了 qqq 次。请你帮小基计算操作结束后所有元素之和。由于答案过大,请对 109+710^9+7109+7 取模。
输入描述
第一行输入两个正整数 nnn, qqq,代表数组的大小和操作次数。
第二行输入 nnn 个正整数 aia_iai,代表数组的元素。
接下来的 qqq 行,每行输入一个正整数 xix_ixi,代表第 iii 次操作未被翻倍的元素。
数据范围:
-
1≤n,q≤1051 \leq n,q \leq 10^51≤n,q≤105
-
1≤xi≤n1 \leq x_i \leq n1≤xi≤n
-
1≤ai≤1091 \leq a_i \leq 10^91≤ai≤109
输出描述
一个整数,代表操作结束后所有元素之和模 109+710^9+7109+7 的值。
样例1
输入:
|---------|---------------------------------|
| 1 2 3 4 | 4
2
1
2
3
4
1
2
|
输出:
|---|------|
| 1 | 34
|

二、分析
在解决这个问题的时候,我首先需要仔细理解题目要求,明确我们的目标是计算在多次操作后数组所有元素的和,并且要对结果进行取模操作。每次操作会将除了指定元素外的所有其他元素翻倍,这样的操作重复多次后,元素的值会迅速增大,所以直接模拟每次操作是不现实的,尤其是当操作次数和数组大小都很大的时候。
接下来,我思考如何高效地计算最终的结果。考虑到直接模拟操作会导致时间复杂度过高,我得寻找一种数学上的优化方法。我意识到,每次操作可以看作是对所有元素(除了一个特定元素)进行乘以2的操作。这样,经过多次操作后,每个元素的值实际上是初始值乘以2的若干次方。关键在于如何确定每个元素被乘以2的次数。我想到,可以用一个数组来记录每个元素被翻倍的次数。每次操作时,除了一个特定元素外,其他所有元素的翻倍次数都加1。这样,经过所有操作后,每个元素的最终值就是初始值乘以2的对应次数方。

在实际操作中,我需要初始化一个数组来记录每个元素的翻倍次数,初始值都为0。然后,对于每一次操作,除了指定的元素外,其他所有元素的翻倍次数都加1。最后,遍历整个数组,计算每个元素的值(初始值乘以2的翻倍次数方),并将它们相加,得到最终的和。在计算过程中,我需要注意数值的范围,因为每次翻倍操作可能导致数值非常大。题目要求对结果进行取模操作,所以我需要在计算过程中不断对中间结果取模,以防止数值溢出。
此外,我还要考虑到输入数据的规模。数组的大小和操作次数都可能达到10^5,这要求我的算法在时间复杂度上必须足够高效。通过上述方法,我可以将时间复杂度控制在O(n + q)级别,其中n是数组的大小,q是操作的次数。最后,我需要验证我的思路是否正确。通过样例输入,我可以检查我的算法是否能够得到正确的输出。如果验证通过,那么我的算法就可以应用到实际问题中。
三、代码
这段代码是为了解决小基数组翻倍操作的问题。我们首先读取输入数据,包括数组的大小、操作次数以及数组的元素。然后我们创建一个数组来记录每个元素被翻倍的次数。在每次操作中,我们指定一个元素不被翻倍,而其他所有元素的翻倍次数加1。我们通过循环来处理所有的操作,并更新每个元素的翻倍次数。最后,我们计算每个元素的最终值,即初始值乘以2的翻倍次数次方,并将所有元素的值相加,得到最终的和。由于结果可能很大,我们在计算过程中对结果进行取模操作,以确保不会溢出。
python
MOD = 10**9 + 7
# 读取输入
n, q = map(int, input().split())
a = list(map(int, input().split()))
times = [0] * n # 记录每个元素被翻倍的次数
for _ in range(q):
x = int(input()) - 1 # 将xi转换为0-based索引
for i in range(n):
if i != x:
times[i] += 1
# 计算每个元素的最终值并求和
total = 0
for i in range(n):
total = (total + a[i] * (2 ** times[i])) % MOD
print(total)
然而,这段代码的时间复杂度是O(q*n),当q和n都很大时,比如都是1e5,这样的时间复杂度会非常高,达到1e10,这在实际应用中会导致超时。因此,我们需要寻找一个更优化的算法。但在这个问题中,由于每次操作需要更新除了一个特定元素外的所有其他元素的翻倍次数,这样的操作很难被优化到O(1)的时间复杂度。因此,这个简单的解决方案在这种情况下可能并不适用。
更优化的解决方案可能需要利用数学技巧或数据结构来减少每次操作的时间复杂度。例如,可以使用一个全局变量来记录所有元素的公共翻倍次数,并使用一个数组来记录每个元素相对于全局翻倍次数的额外翻倍次数。这样每次操作可以只更新全局翻倍次数和特定元素的额外翻倍次数,而不是遍历整个数组。但这样的优化需要更深入的算法设计和数学推导,并且代码实现也更为复杂。但考虑到题目中的数据范围可能很大,直接使用上述简单方法在实际中可能不可行,必须采用更高效的方式。
四、代码优化
我们可以引入一个变量global_times来记录所有元素共同经历的翻倍次数,以及一个数组individual_times来记录每个元素相对于global_times的额外翻倍次数。
每次操作时,指定第x个元素不被翻倍,那么相当于:
- 所有元素(除了x)的翻倍次数加1。这可以视为global_times加1,同时x元素的individual_times减1(因为x元素的总翻倍次数应该是global_times + individual_times[x],而其他元素则是global_times + individual_times[其他元素])。
这样,每次操作只需要更新global_times和individual_times[x],时间复杂度为O(1)。当计算每个元素的最终值时,每个元素的总翻倍次数是global_times + individual_times[i]。这样的优化将每次操作的时间复杂度降低到O(1),整个算法的时间复杂度变为O(n + q),这在处理大数据时非常高效。但这样的优化需要更仔细的数学推导和代码实现,并且需要确保在取模运算时正确处理指数运算。
以下是优化后的代码:
python
MOD = 10**9 + 7
# 读取输入
n, q = map(int, input().split())
a = list(map(int, input().split()))
individual_times = [0] * n # 每个元素相对于global_times的额外翻倍次数
global_times = 0 # 所有元素共同经历的翻倍次数
for _ in range(q):
x = int(input()) - 1 # 将xi转换为0-based索引
global_times += 1
individual_times[x] -= 1 # 因为x元素不被翻倍,所以它的额外翻倍次数减1
# 计算每个元素的最终值并求和
total = 0
for i in range(n):
total_times = global_times + individual_times[i]
total = (total + a[i] * pow(2, total_times, MOD)) % MOD
print(total)
这个优化后的方案大大提高了处理大数据的能力,使其能够在合理的时间内完成计算。