https://codeforces.com/contest/1333/problem/C
定义一个'good'数组:数组的子数组前缀和不为零;
题目给定一个数组 a[i],求数组中满足'good'数组的子数组个数;
做这题的时候忘了前缀和的一个性质(以前用到过),真tm傻逼啊,然后就一直不会,啊啊啊啊,这tm都不会,你不掉分谁掉分,掉分一时爽,一直掉分一直爽!!!
看了别人代码模拟了一下,想起来了,,,,
如果某个前缀出现了和前面一个前缀一样的和,那么中间的数的和为0;
比如a1=1, a2=1, a3=2, a4=-3; sum[i] 为前缀和数组, sum[1]=1, sum[2]=2, sum[3]=4, sum[4]=1;
sum[1]==sum[4], 那么 [2,4] 的区间前缀和为0.
知道了这个性质就很简单了,用一个map数组维护当前出现过的前缀和的下标,用pos来记录当前没有前缀和为0的区间的左端点(pos要取max,如果有两个前缀和为0的数组成呈包含关系,例如 1,2,3,-5,-1,这个样例如果不取max就有问题),然后每次计算就可以了,满足条件的区间就是 [pos,i],然后区间内数的个数就是增加当前这个点后增加的'good'数组个数,注意一点边界问题。
#include <bits/stdc++.h> using namespace std; const int MAXN=2e5+5; const int mod=1e9+7; typedef long long ll; //typedef __int128 LL; const int inf=0x3f3f3f3f; const long long INF=0x3f3f3f3f3f3f3f3f; ll a[MAXN],sum[MAXN]; map<ll,ll>mp;//mp记录前缀和的位置 int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; } ll ans=0; ll pos=1;//pos记录前缀和不为零的区间左端点 mp[0]=0;//把0记录下来,第一个数前面的前缀和为0 for(int i=1;i<=n;i++) { if(mp.count(sum[i]))pos=max(pos,mp[sum[i]]+2); mp[sum[i]]=i; ans+=i-pos+1; } printf("%lld\n",ans); return 0; }