題意
給定一個長度為 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;
}