这题 嗯.......可能我还是刷的太少了——我感觉真的好巧妙啊,我压根都没想到前缀和还有这种作用,果然菜就的多练,一起加油吧 qwq -^_^-
pp废话不多说,我开始讲解思路了:我们看到题目大意其实就是要我们去分割区间,让每一个小区间的和为0,并且使得区间和为零的区间的个数最多。
了解了题目的大概意思之后,我就想到用前缀和,但我不知道怎么能够一直更新我的指针的位置,就卡住了,下面我讲解一下我看的代码的神之操作:首先你可以运用前缀和数组,其实用一个数也可以,因为我们这次的前缀和用法不同,所以可以直接用sum记录前缀和的值,我们先要了解清楚,我们怎么去划分才能得到更多的区间和为零的区间个数,一定是让区间越小越好,假设数组为:【1,0,-1】,那么当有个数为0时,我们一定是直接把它当作一个区间,而不是去考虑一整个-1,0,1,那么我们要如何去进行这个操作呢,同时又要避免重叠性问题,我们可以直接用一个记录索引的变量index来记录我们前一个美丽子区间的右边界的索引在多少,当我得到0是美丽的子区间的时候,我同时更新index=i,这样能够避免我下一步又会把-1,0,1这个区间算进去。
下面我讲一下最妙的地方:就是他用了map作为哈希表,当我得到一个新的前缀和的时候,我现在map中判断,有没有出现过,如果出现过,并且它的索引>=我的上一个最美子区间的位置,这就说明我找到了一个新的子区间,cnt++,一旦等于的话,其实是我的特殊情况,就是我在最开始就把0给塞进去,map[0]=-1,我的index也是初始化为-1,这个时候当我第一个数为0,是出现了相等的情况,你们可以好好捋一下,我一开始也是有点不理解,后面就是一整个恍然大悟.
具体实现代码:
#include <iostream>
#include <cstring>
#include <map>
using namespace std;
const int N = 3e5+10;
long long t,n;
long long a[N];
map<long long,long long> mp;
int main(void){
cin>>t;
long long cnt=0,sum=0;
long long index=-1; //记录上一个前缀和为0的区间的边界位置
while(t--){
cin>>n;
memset(a,0,sizeof(a));
mp.clear();
mp[0]=-1;
index=-1;
cnt=0;
sum=0;
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
if((mp.find(sum)!=mp.end())&&(mp[sum]>=index)){
cnt++;
index=i;
}
mp[sum]=i;
}
cout<<cnt<<endl;
}
return 0;
}