- 题目:
- 思路:
枚举右端点,统计有多少个左端点和当前右端点组成的区间不是good,总数记为 c n t cnt cnt。-
对于一个确定的区间 [ l , r ] [l,r] [l,r],如果 s u m [ r ] = s u m [ l − 1 ] sum[r]=sum[l-1] sum[r]=sum[l−1],那么该区间内元素和为0, s u m [ ] sum[] sum[]表示前缀和。
-
统计 s u m [ r ] sum[r] sum[r]最新出现的位置 p o s pos pos:
- 如果 s u m [ r ] sum[r] sum[r]未出现过,那么不存在 l l l,使得 [ l , r ] [l,r] [l,r]的区间和为0,当前 r r r对答案的贡献即上一个 r r r对答案的贡献。
- 如果 s u m [ r ] sum[r] sum[r]出现过,那么 [ p o s + 1 , r ] [pos+1,r] [pos+1,r]内的区间和为0,且对于 l ≤ p o s , [ l , r ] l≤pos,[l,r] l≤pos,[l,r]都是不是good。注意,可能出现 [ p o s + 1 , r ] [pos+1,r] [pos+1,r]内某个子区间和为0,且该子区间的左边界为 L L L,那么更新 L = m a x ( L , p o s + 1 ) L=max(L,pos+1) L=max(L,pos+1),当前 r r r对答案的贡献为 L L L
-
a n s = n ∗ ( n + 1 ) 2 − c n t ans=\frac{n*(n+1)}{2}-cnt ans=2n∗(n+1)−cnt
-
- ac代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const int maxn = 2e5+10;
int n;
ll cnt[maxn];
map<ll, int> last;
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
scanf("%d", &n);
ll x, sum = 0, ans = 0;
int left0 = 0;
last[0] = 0;
for(int i = 1; i <= n; i++)
{
scanf("%lld", &x);
sum += x;
if(!last.count(sum)) cnt[i] = cnt[i-1];
else
{
left0 = max(left0, last[sum]+1);
cnt[i] = left0;
}
last[sum] = i;
ans += cnt[i];
}
printf("%lld\n", 1ll*n*(n+1)/2-ans);
return 0;
}
/*
5
2 3 0 1 -4
8
2 3 0 1 2 1 -3 -4
*/