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;
}
View Code

 

posted on 2020-04-09 14:48  MZRONG  阅读(166)  评论(0编辑  收藏  举报