2022-05-03每日刷题打卡
代码源——每日一题
简单子段和 - 题目 - Daimayuan Online Judge
给出一个长为 N 的整数数组 A 和一个整数 K。
请问有数组 A 中有多少个子数组,其元素之和为 K?
输入格式
第一行两个整数 N 和 K,表示数组 A 的大小,和给出的整数 K。
第二行 N 个整数,表示数组 A 中的每个元素 A1,…,An。
输出格式
输出一个整数,表示答案。
样例输入1
6 5
8 -3 5 7 0 -4
样例输出1
3
有三个子数组 (A1,A2),(A3),(A2,…,A6)满足条件。
首先题目要的是子数组的和,那么就是连续的几个数,他们的和要等于k。
最朴素的做法就是枚举区间长度和区间左端点,然后计算区间和,如果和等于k,那就符合要求,计数器++。但这样的复杂度是n^3,太慢了,我们有没有什么办法可以优化一下?
要想快速求一个区间的区间和,自然想到前缀和,我们准备出所给数组的前缀和数组s,然后只要用s®-s(l-1)就可以快速得到l~r区间的区间和了,这样时间复杂度就变成了n^ 2,我们只用枚举区间长度和左端点的位置再通过前缀和数组判断这个区间和是否为k即可,但n^2显然也过不了2e5的数据量,所以我们需要再次优化一下。
经过前缀和数组的优化后,现在问题变成了,在前缀和数组里找两个数,他们相减的差值为k。标准的两数之差问题,数组里两个数a,b他们满足a-b=k,那么也可以变成a-k=b,我们可以把这个问题转化一下:在数组里,假设当前数的值是a,那么在这个数位置的前面,有多少个a-k的值的数?为什么是前面?因为前缀和算区间和是右端点的值减去左端点-1的值,我们缺认了右端点,那么自然就要找左端点。那么我们只要算前缀和的时候,把前缀和的值存入哈希表,然后每次在哈希表中找是否有当前前缀和-k的值存在,如果存在,则计数器加上哈希表存的个数。
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 5e4 + 50;
const int MOD = 1e9 + 7;
inline ll read() {
ll x = 0; char ch = getchar();
int f = 1;
if (ch == '-')f = -1;
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
inline void write(ll x) {
if (x > 9) write(x / 10);
putchar(x % 10 | '0');
}
int main()
{
unordered_map<ll, ll>mymap;
mymap[0] = 1;
ll n = read(), k = read(), x, sum = 0, res = 0;
for (int i = 0; i < n; i++)
{
x = read();
sum += x;
res += mymap[sum - k];
mymap[sum]++;
}
write(res);
return 0;
}