题意
给定一个长度为 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<<'
';
return 0;
}