题解:学而思编程 阶梯求和
【题目来源】
学而思编程:阶梯求和
【题目描述】
猴博士定义了一个"阶梯和"的概念------简单来说,对于一个数组 \(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
【核心思想】
-
问题分析:给定数组 \(a_1, a_2, \ldots, a_n\),定义区间 \([L, R]\) 的"阶梯和"为 \(\sum_{i=L}^{R} (i - L + 1) \times a_i\),即区间内第 \(k\) 个元素的权重为 \(k\)。\(m\) 次询问求各区间阶梯和。这是一个双前缀和 + 公式推导问题,关键在于将带权求和转化为普通前缀和与加权前缀和的组合。
-
算法选择:
- 普通前缀和:
sum1[i] = a_1 + a_2 + \ldots + a_i - 加权前缀和:
sum2[i] = 1 \times a_1 + 2 \times a_2 + \ldots + i \times a_i - 公式转化:将阶梯和转化为
sum2与sum1的线性组合
- 普通前缀和:
-
关键步骤:
- 初始化:读取 \(n\)(数组长度)和 \(m\)(询问次数)
- 构建双前缀和:
- 遍历 \(i\) 从 \(1\) 到 \(n\):
sum1[i] = sum1[i-1] + a_i:普通前缀和sum2[i] = sum2[i-1] + i \times a_i:加权前缀和(下标 \(i\) 作为权重)
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 处理 \(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
-
时间/空间复杂度:
- 时间复杂度:\(O(n + m)\),预处理 \(O(n)\),每次询问 \(O(1)\)
- 空间复杂度:\(O(n)\),两个前缀和数组
sum1[1..n]和sum2[1..n]
-
双前缀和的核心思想:
- 权重偏移转化:阶梯和的权重 \((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
浙公网安备 33010602011771号