题解:AtCoder AT_awc0004_e Sum of Intervals
【题目来源】
AtCoder:E - Sum of Intervals
【题目描述】
Takahashi has a sequence of \(N\) integers \(A = (A_1, A_2, \ldots, A_N)\). Each element \(A_i\) may take a negative value.
高桥有一个包含 \(N\) 个整数的序列 \(A = (A_1, A_2, …, A_N)\)。每个元素 \(A_i\) 可能取负值。
Takahashi wants to select a contiguous subarray from this sequence such that the sum of its elements is exactly equal to the integer \(K\), where \(K\) is the target sum value. Find the number of ways to choose such a subarray.
高桥希望从该序列中选择一个连续子数组,使得其元素之和恰好等于整数 \(K\)(其中 \(K\) 是目标和值)。求选择这样的子数组的方式数量。
More precisely, find the number of pairs of integers \((l, r)\) satisfying \(1 \leq l \leq r \leq N\) such that
更精确地说,求满足 \(1 ≤ l ≤ r ≤ N\) 且满足以下条件的整数对 \((l, r)\) 的数量:
【输入】
\(N\) \(K\)
\(A_1\) \(A_2\) \(\cdots\) \(A_N\)
- The first line contains the integer \(N\) representing the number of elements in the sequence and the integer \(K\) representing the target sum value, separated by a space.
- The second line contains the integers \(A_1, A_2, \ldots, A_N\) representing each element of the sequence, separated by spaces.
【输出】
Print the number of pairs of integers \((l, r)\) that satisfy the condition, on a single line.
【输入样例】
5 5
1 2 3 4 5
【输出样例】
2
【核心思想】
-
问题分析:给定一个长度为 \(N\) 的整数序列 \(A_1, A_2, \ldots, A_N\)(可能包含负数),需要找出有多少个连续子数组的元素之和恰好等于 \(K\)。这是一个经典的前缀和问题,可以通过转化公式将区间和查询转化为两个前缀和的差。
-
算法选择:
- 前缀和:将区间和转化为前缀和的差,即 \(sum(l, r) = sa[r] - sa[l-1]\)
- 哈希表(HashMap):存储每个前缀和出现的次数,实现 \(O(1)\) 查询
- 转化公式:\(sa[r] - sa[l-1] = K \Leftrightarrow sa[l-1] = sa[r] - K\)
-
关键步骤:
- 初始化:
mp[0] = 1,表示前缀和为 \(0\) 出现 \(1\) 次(对应空子数组) - 遍历数组(从 \(i = 1\) 到 \(N\)):
- 计算前缀和 \(sa[i] = sa[i-1] + A_i\)
- 查找匹配:查询哈希表中前缀和为 \(sa[i] - K\) 的出现次数,累加到答案 \(ans\)
- 若 \(mp[sa[i] - K] = c\),说明存在 \(c\) 个位置 \(j\) 满足 \(sa[j] = sa[i] - K\)
- 即存在 \(c\) 个子数组 \([j+1, i]\) 的和为 \(K\)
- 更新哈希表:
mp[sa[i]]++,将当前前缀和加入哈希表
- 输出答案 \(ans\)
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(N \log N)\) 或 \(O(N)\)(取决于哈希表实现),遍历数组一次,每次操作 \(O(1)\)
- 空间复杂度:\(O(N)\),哈希表最多存储 \(N+1\) 个不同的前缀和
-
前缀和与哈希表的核心思想:
- 区间和转化:利用前缀和将区间和查询转化为两个前缀和的差,即 \(\sum_{i=l}^{r} A_i = sa[r] - sa[l-1]\)
- 两数之差问题:将"找区间和等于 \(K\)"转化为"找两个前缀和之差等于 \(K\)",再转化为"对于每个 \(sa[r]\),找有多少个 \(sa[l-1] = sa[r] - K\)"
- 哈希表优化:用哈希表存储前缀和频率,将查找时间从 \(O(N)\) 优化到 \(O(1)\)
- 初始化技巧:
mp[0] = 1处理从数组开头开始的子数组(即 \(l = 1\) 的情况) - 适用于子数组求和、区间统计、两数之和/差等问题
【解题思路】

【算法标签】
前缀和
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
int n, k; // n: 数组长度,k: 目标和
int a[N], sa[N], ans; // a: 原始数组,sa: 前缀和数组,ans: 计数结果
map<int, int> mp; // 用于存储前缀和出现的次数
signed main()
{
cin >> n >> k; // 读入数组长度和目标和
mp[0] = 1; // 初始化:前缀和为0出现1次(空子数组)
for (int i = 1; i <= n; i++)
{
cin >> a[i]; // 读入数组元素
sa[i] = sa[i - 1] + a[i]; // 计算前缀和
// 如果存在前缀和为sa[i]-k,则说明找到了和为k的子数组
ans += mp[sa[i] - k];
// 将当前前缀和加入map
mp[sa[i]]++;
}
cout << ans << endl; // 输出和为k的子数组个数
return 0;
}
【运行结果】
5 5
1 2 3 4 5
2
浙公网安备 33010602011771号