||:
刚开始一直想的是用总的数量减去不合法的数量,其实会有很多重复,应该直接加合法的。
思路:
- now表示: s[l],s[l+1],s[l+2],s[r-1],s[r]
- x表示:第 i 输入的数字
- sum表示:前 i 个数字的和(sum=sum+x)
- k表示:最后一个出现有等于0子段的位置( 防止重复 )
先清楚一点:如果 now合法,那么对于now会有(1+2+3+4+…+cnt )种答案.
那么问题就转变成找合法段, 用map来记录下sum,
map[sum] 会有2种情况 1: 当map[sum] >= k 的时候,说明出现了一个 不重叠的 和为0的子段,那这个 now 的第一个值 就是(i-map[sum]-1), 我们更新k ,因为k 前面的 合法子段 我们已经全部统计了。 2: 当map[sum]<k的时候,虽然 这个now 合为0 ,但是 前k个已经算上了,然后 k后面的 i-k个 是合法的,所以这个时候 cnt++;
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
map<ll,int>m;
int main(){
int n;
scanf("%d",&n);
m[0]=1;
ll x,sum=0,ans=0,cnt=0,k=0;
for(int i=2;i<=n+1;++i){
scanf("%lld",&x);
sum+=x;
// cout<<"$$$$$$$$$$$$$$$$$$$$$ "<<k<<endl;
if(m[sum]>=k){
k=m[sum];
cnt=i-m[sum]-1;
}else{
++cnt;
}
ans+=cnt;
// cout<<"@@@ "<<cnt<<" %%% "<<ans<<"*** "<<m[sum]<<"&*&* "<<sum<<endl;
m[sum]=i;
}
printf("%lld\n",ans);
return 0;
}