这里写自定义目录标题
更多精彩内容
这里是带你游历编程世界的Dashcoding编程社,我是Dash/北航硕士/ICPC区域赛全国排名30+/给你呈现我们眼中的世界!
256题算法特训课,帮你斩获大厂60W年薪offer
原题
美团校招真题-小美与数组
B站动画详解
问题分析
在这道题目中,我们需要计算在经过多次操作后,数组所有元素的最终和。每次操作中,只有一个指定的元素保持不变,其余所有元素都会被翻倍。这意味着我们不能简单地模拟每次操作的结果,因为这样会导致时间复杂度过高,尤其是在操作次数 q q q 和数组大小 n n n 都很大的情况下。更高效的方式是通过差分数组来记录每个元素的翻倍次数,再通过快速幂来计算每个元素的最终值,并求出这些值的总和。同时,由于操作次数可能非常大,且数组元素的初始值也可能很大,因此在输出结果时需要对 1 0 9 + 7 10^9 + 7 109+7 取模。这道题目综合了差分数组、快速幂和模运算的知识点,考察了算法设计的高效性和准确性。
思路分析
首先,我们需要利用差分数组来记录每个元素的翻倍次数。差分数组是一种常见的技巧,它通过在某个位置进行标记,表示从这一位置开始,后续的一系列元素需要被执行相同的操作。具体来说,每次操作时,我们在差分数组中增加两个标记,一个标记从数组的第一个元素开始,表示该元素之后的所有元素都需要翻倍;另一个标记在当前操作所指定的元素位置之后,表示从此位置之后的元素不再进行翻倍。通过这种方式,我们可以快速地统计出每个元素的最终翻倍次数。接下来,我们使用快速幂的方法来计算每个元素经过翻倍后的值。快速幂的思想是通过不断地将指数进行二分,以减少计算的次数,从而高效地求出 2 k m o d ( 1 0 9 + 7 ) 2^k \mod (10^9 + 7) 2kmod(109+7) 的值。最后,将所有元素的翻倍值累加起来,得到最终的总和并对 1 0 9 + 7 10^9 + 7 109+7 取模,以避免结果过大。
算法实现
算法的实现可以分为几个主要步骤。首先,我们读取输入的数组和操作次数,然后初始化差分数组。接下来,对于每次操作,我们根据给定的位置更新差分数组。在操作完成后,通过对差分数组求前缀和的方式,计算出每个元素的翻倍次数。然后,我们利用快速幂函数计算每个元素的最终值,并将这些值累加起来得到总和。最后,对总和进行取模运算,并输出结果。在实现过程中,需要注意的是,由于数组大小和操作次数都可能非常大,因此所有的计算都必须在取模运算下进行,避免整数溢出。这个算法的核心在于如何高效地处理大规模的数据操作,以及如何通过数学方法来简化计算过程。
代码详解
在这段代码中,qpow 函数用于计算
2
k
m
o
d
(
1
0
9
+
7
)
2^k \mod (10^9 + 7)
2kmod(109+7),以高效地处理大规模的翻倍操作。主函数中,我们首先读取输入并初始化数组,然后使用差分数组来处理每次操作。通过对差分数组的累加,我们得到了每个元素的最终翻倍次数。最后,通过快速幂计算出每个元素的最终值,并累加得到总和,输出时取模
1
0
9
+
7
10^9 + 7
109+7。
标准代码程序
C++代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll a[N],f[N],mod=1e9+7;
// 快速幂函数,用于计算 2^n % mod
ll qpow(ll n)
{
ll base=2,ans=1;
while(n)
{
if(n&1) ans=ans%mod*base%mod;
base=base%mod*base%mod;
n>>=1;
}
return ans%mod;
}
int main()
{
int n,q;
cin>>n>>q;
ll ans=0;
// 读取数组元素
for(int i=1;i<=n;i++) cin>>a[i];
// 处理每次操作并更新差分数组
for(int i=1;i<=q;i++)
{
int id;
cin>>id;
f[1]++; // 从第1个元素开始增加
f[id]--; // 从id位置的元素不翻倍
f[id+1]++; // 从id+1位置的元素开始增加
}
// 通过差分数组求出每个位置的最终翻倍次数
for(int i=1;i<=n;i++) f[i]+=f[i-1];
// 计算最终的元素和,并对mod取模
for(int i=1;i<=n;i++)
{
ans=ans%mod+a[i]*qpow(f[i])%mod;
ans%=mod;
}
cout<<ans%mod;
}
Java代码
import java.util.Scanner;
public class Main {
static final int MOD = 1000000007;
public static long qpow(long n) {
long base = 2, ans = 1;
while (n > 0) {
if ((n & 1) == 1) ans = ans * base % MOD;
base = base * base % MOD;
n >>= 1;
}
return ans % MOD;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(), q = scanner.nextInt();
long[] a = new long[n + 1];
long[] f = new long[n + 2];
for (int i = 1; i <= n; i++) a[i] = scanner.nextLong();
for (int i = 0; i < q; i++) {
int id = scanner.nextInt();
f[1]++;
f[id]--;
f[id + 1]++;
}
long ans = 0;
for (int i = 1; i <= n; i++) f[i] += f[i - 1];
for (int i = 1; i <= n; i++) {
ans = (ans + a[i] * qpow(f[i]) % MOD) % MOD;
}
System.out.println(ans);
scanner.close();
}
}
Python代码
MOD = 10**9 + 7
def qpow(n):
base, ans = 2, 1
while n > 0:
if n & 1:
ans = ans * base % MOD
base = base * base % MOD
n >>= 1
return ans % MOD
n, q = map(int, input().split())
a = list(map(int, input().split()))
f = [0] * (n + 2)
for _ in range(q):
id = int(input())
f[0] += 1
f[id - 1] -= 1
if id < n:
f[id] += 1
ans = 0
for i in range(1, n + 1):
f[i] += f[i - 1]
for i in range(n):
ans = (ans + a[i] * qpow(f[i])) % MOD
print(ans)
Javascript代码
const MOD = 1000000007;
function qpow(n) {
let base = 2, ans = 1;
while (n > 0) {
if (n & 1) ans = ans * base % MOD;
base = base * base % MOD;
n >>= 1;
}
return ans % MOD;
}
function main() {
const [n, q] = prompt().split(' ').map(Number);
const a = prompt().split(' ').map(Number);
const f = Array(n + 2).fill(0);
for (let i = 0; i < q; i++) {
const id = Number(prompt());
f[0]++;
f[id - 1]--;
if (id < n)
f[id]++;
}
let ans = 0;
for (let i = 1; i <= n; i++) {
f[i] += f[i - 1];
}
for (let i = 0; i < n; i++) {
ans = (ans + a[i] * qpow(f[i])) % MOD;
}
console.log(ans);
}
main();
复杂度分析
在这道题目中,算法的时间复杂度主要由几部分构成。首先是差分数组的初始化和更新,这一部分的复杂度是 O ( q ) O(q) O(q),其中 q q q 是操作的次数。接着,我们需要遍历整个数组以计算每个元素的最终翻倍次数,这部分的复杂度是 O ( n ) O(n) O(n)。然后是使用快速幂计算每个元素的值,这部分的复杂度是 O ( log k ) O(\log k) O(logk),但由于所有操作的翻倍次数相同,因此整体复杂度仍为 O ( n ) O(n) O(n)。最后,累加和取模的过程也是 O ( n ) O(n) O(n) 的。因此,整个算法的时间复杂度为 O ( n + q ) O(n + q) O(n+q),对于题目给定的数据范围( n , q ≤ 1 0 5 n, q \leq 10^5 n,q≤105),这一复杂度是可以接受的。在空间复杂度方面,我们使用了额外的差分数组来记录翻倍次数,因此空间复杂度为 O ( n ) O(n) O(n)。综合来看,这道题目通过巧妙地使用差分数组和快速幂,实现了在较高效的时间和空间范围内解决问题的目标。
总结
这道题目通过差分数组和快速幂的结合,考察了对大规模数据的高效处理能力。差分数组帮助我们快速标记和计算每个元素的操作次数,而快速幂则用于计算大指数下的幂次结果,避免了直接模拟带来的时间复杂度过高的问题。通过取模操作,确保了最终结果不会超出限制范围。整体来看,这道题目不仅考察了算法设计,还需要一定的数学基础,在处理类似大规模数据操作的问题时,能够提供有效的思路和方法。

被折叠的 条评论
为什么被折叠?



