【888题秋招篇】剑指大厂offer第二题,带你用差分快速幂秒杀美团校招真题-小美与数组,斩获大厂年薪60wOffer

更多精彩内容

这里是带你游历编程世界的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,q105),这一复杂度是可以接受的。在空间复杂度方面,我们使用了额外的差分数组来记录翻倍次数,因此空间复杂度为 O ( n ) O(n) O(n)。综合来看,这道题目通过巧妙地使用差分数组和快速幂,实现了在较高效的时间和空间范围内解决问题的目标。

总结

这道题目通过差分数组和快速幂的结合,考察了对大规模数据的高效处理能力。差分数组帮助我们快速标记和计算每个元素的操作次数,而快速幂则用于计算大指数下的幂次结果,避免了直接模拟带来的时间复杂度过高的问题。通过取模操作,确保了最终结果不会超出限制范围。整体来看,这道题目不仅考察了算法设计,还需要一定的数学基础,在处理类似大规模数据操作的问题时,能够提供有效的思路和方法。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值