Codeforces 1333C - Eugene and an array(前缀和)


题意

给定一个长度为 n 的数组 ar (n<=2e5)

问这个数组 ar 中有多少子数组是好数组

子数组的定义为:

  把一个数组前面删去0个或全部元素,后面删去0个或全部元素得到的数组就是原数组的子数组

好数组的定义为:

  对于数组 a 的每个子数组 b 都满足 sum{b} ≠ 0

  则数组 a 就是个好数组

每个数组元素保证 abs(a[i])<=1e9




解题思路

假设我们找到了一个和为0的子数组 br

那么只要有其他子数组包含br,那么这些子数组都不能称作好数组


所以我们在处理某个元素ar[i]时,只看以ar[i]为右边界的子数组,r=i

从 i 位置开始往左寻找,直到将某个和为0的子数组包含进来后停止,令此时左边界为 l

那么 r-l 就是以ar[i]为右边界好数组的个数(包含[r,r]、不包含[l,r])

所以只要记录下 1 到 i 这段区间中,和为0的子数组的最大左边界maxL即可

每次答案加上 i-maxL


那么对于和为0的子数组的判断,记录前缀和是否出现过进行判断

比如[1,x]的和为a,[1,y]的和也为a,那就说明[x+1,y]这段子数组和为0

因为前缀和分布离散,所以使用map/unordered_map进行储存


总体时间复杂度为O(nlogn)




程序

map中未出现的key的value默认为0,可以用来判断某个前缀和是否出现过

注意刚开始一个元素都没有的时候也要记录下前缀和为0,否则第一个从1开始和为0的子数组将无法进行判断

因为有可能出现两个和为0的子数组呈包含关系(例如1 1 -1 -1,[1,4]和[2,3]和为0),这种情况下如果不对maxL进行取大,可能原本maxL为2,在执行到 i=4 时被小值取代变成maxL=1,与想法不符

最后,注意长整型

(186ms/1500ms)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

map<ll,int> mp;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int n;
    ll ar,sum=0,ans=0,maxL=1;
    cin>>n;
    mp[0]=1;//初始前缀和为0
    for(int i=2;i<=n+1;i++)
    {
        cin>>ar;
        sum+=ar;
        if(mp[sum]!=0)
            maxL=max(maxL,ll(mp[sum]+1)); //记得取大(否则wa8……)
        ans+=i-maxL;//以i为右边界,满足题意的个数
        mp[sum]=i;
    }
    cout<<ans<<'\n';

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/stelayuri/p/12664224.html