CF-Round #632-div2-C题
C. Eugene and an array
这道题是思维,双指针~(嘻,又来到双指针啦)
题目大意:题目给定good子数组的含义。
如果此数组的任意的子数组的和都不为0,那么我们称这个数组为good数组。题目要求输出good数组的个数。
这道题首先想到的就是:先维护一波前缀和啦~
从前缀和的关系中维护我们的答案ans.
这个过程使用双指针。
自己模拟一下样例就很容易发现。要使得当前处理的数组中的子数组也是good数组的话。那么对应的前缀和也就不能出现重复的和。相同的前缀和对应到原数组中就是存在和为零的情况。
下标: 1 2 3 4
比如: 1 2 -3 1
前缀和:1 3 0 1
遇到相同的前缀和的时候比如sum[1]和sum[4],sum[4] - sum[1] = a[2] + a[3] + a[4] = 0;(前缀和相减是区间和,这个应该很容易)
这种情况是不满足要求的。所以我们让l++
l记录的是good数组左端对应的前一个下标
r记录的是good数组的右端对应的下标
所以初始化的时候l = 0, r = 1;(因为我存数据的时候是从1开始的哈)
用map来标记当前前缀和是否出现过。前缀和可能有正有负,所以不能用原先普通的数组。
map<ll, bool> vis;
首先初始化vis[0] = 1;
因为0是不允许出现的(不满足条件的)
最后我们维护一下ans:
ans += r - l就行。
对应的good数组为:
a[l + 1] ~ a[r];
a[l + 2] ~ a[r];
a[l + 3] ~ a[r];
…
a[r] ~ a[r];
一共(r - l)个。
代码部分很简洁~:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll sum[N];
map<ll, bool> vis;
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
ll t;
scanf ("%I64d", &t);
sum[i] = sum[i - 1] + t;
}
vis[0] = true;
ll ans = 0;
int l = 0, r = 1;
while (r <= n)
{
while (vis[sum[r]])
{
vis[sum[l]] = false;
l++;
}
vis[sum[r]] = true;
ans += r - l;
r++;
}
cout << ans << endl;
return 0;
}