题解:学而思编程 阶梯求和

【题目来源】

学而思编程:阶梯求和

【题目描述】

猴博士定义了一个"阶梯和"的概念------简单来说,对于一个数组 \(a_1,...,a_n\),它的数组阶梯和就是:\(1 \times a_1,...,n \times a_n\)

现在给你一个数组 \(a_1,...,a_n\),然后猴博士会给你 \(m\) 个区间 \([L_i,R_i]\),请你依次求出其区间内数组的"阶梯和"。

例如,对于数组 \(1,2,3,4\)

  • 区间 \([2,4]\) 的"阶梯和"为:\(1 \times 2 + 2 \times 3 + 3 \times 4 = 20\)
  • 区间 \([1,2]\) 的"阶梯和"为:\(1 \times 1 + 2 \times 2 = 5\)

【输入】

第一行两个正整数 \(n,m\)

第二行 \(n\) 个整数 \(a_1,...,a_n\)

接着 \(m\) 行,每行两个正整数 \(L_i,R_i\),表示需要求解的区间。

【输出】

\(m\) 行,每行一个整数表示答案。

【输入样例】

4 2
1 2 3 4
2 4
1 2

【输出样例】

20
5

【核心思想】

  1. 问题分析:给定数组 \(a_1, a_2, \ldots, a_n\),定义区间 \([L, R]\) 的"阶梯和"为 \(\sum_{i=L}^{R} (i - L + 1) \times a_i\),即区间内第 \(k\) 个元素的权重为 \(k\)\(m\) 次询问求各区间阶梯和。这是一个双前缀和 + 公式推导问题,关键在于将带权求和转化为普通前缀和与加权前缀和的组合。

  2. 算法选择

    • 普通前缀和sum1[i] = a_1 + a_2 + \ldots + a_i
    • 加权前缀和sum2[i] = 1 \times a_1 + 2 \times a_2 + \ldots + i \times a_i
    • 公式转化:将阶梯和转化为 sum2sum1 的线性组合
  3. 关键步骤

    • 初始化:读取 \(n\)(数组长度)和 \(m\)(询问次数)
    • 构建双前缀和
      • 遍历 \(i\)\(1\)\(n\)
        • sum1[i] = sum1[i-1] + a_i:普通前缀和
        • sum2[i] = sum2[i-1] + i \times a_i:加权前缀和(下标 \(i\) 作为权重)
    • 处理 \(m\) 次询问
      • 读取区间 \([l, r]\)
      • 公式推导
        • 阶梯和 = \(\sum_{i=l}^{r} (i - l + 1) \times a_i = \sum_{i=l}^{r} i \times a_i - (l-1) \times \sum_{i=l}^{r} a_i\)
        • = (sum2[r] - sum2[l-1]) - (l-1) * (sum1[r] - sum1[l-1])
      • ans = sum2[r] - sum2[l-1] - (l-1) * (sum1[r] - sum1[l-1])
      • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n + m)\),预处理 \(O(n)\),每次询问 \(O(1)\)
    • 空间复杂度:\(O(n)\),两个前缀和数组 sum1[1..n]sum2[1..n]
  5. 双前缀和的核心思想

    • 权重偏移转化:阶梯和的权重 \((i - l + 1)\) 可拆分为 \(i - (l-1)\),将变权重转化为固定权重 \(i\) 减去常数 \((l-1)\)
    • 固定权重前缀化sum2 用固定权重 \(i\) 累加,询问时通过减去 \((l-1) \times sum1\) 实现权重偏移,避免每次重新计算
    • 离线预处理:两个前缀和与询问无关,预先计算后每次询问仅需常数时间
    • 公式通用性:该方法可推广至任意线性权重 \(\alpha \times i + \beta\) 的区间求和
    • 适用于"变权重区间求和"类问题,双前缀和将 \(O(n)\) 的区间计算优化到 \(O(1)\)

【算法标签】

前缀和

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, m;
long long sum1[1000005], sum2[1000005];  // sum1: 前缀和, sum2: 加权前缀和

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x;
        sum1[i] = sum1[i - 1] + x;  // 计算普通前缀和
        sum2[i] = sum2[i - 1] + i * x;  // 计算加权前缀和:索引 * 值
    }
    while (m--)
    {
        int l, r;
        cin >> l >> r;
        // 计算公式:sum2[r]-sum2[l-1] - (l-1)*(sum1[r]-sum1[l-1])
        long long ans = sum2[r] - sum2[l - 1] - (l - 1) * (sum1[r] - sum1[l - 1]);
        cout << ans << "\n";
    }
    return 0;
}

【运行结果】

4 2
1 2 3 4
2 4
20
1 2
5
posted @ 2026-07-04 15:10  团爸讲算法  阅读(4)  评论(0)    收藏  举报