Eugene and an array
链接:C题链接
当时没想出来答案!(自己太菜了),看了一些分析后写了一下。
题意:
一个good数组的定义:非空,并且其所有连续非空子数组的和都不等于0
子数组:一个数组本身,或者一个数组中的连续一段的数组,或空数组
比如[0] 不是good数组
[1,-1,5] 不是good数组因为其一个子数组[1,-1]和为0
[1,5,-1] 是good数组
现在给你一个长度为n的数组,求出有多少个good的子数组
数据范围:
1<= n <=2e5
-1e9<= ai <=1e9
思路:
首先所有子序列个数n^2 枚举肯定TLE呀!
关于一段序列长度的和,我们可以想到使用前缀和数组pre[],其中
pre[x]=arr[1]+aar[2]+……+aar[x]
然后我们在运算的时候如果存在pre[a]=k , pre[b]=k (1<=a<b<=n),
i.e. pre[b] - pre[a] = arr[b] + arr[b-1] + …… + arr[a+1] = k - k = 0
也就是区间 Sum[a+1 , b] = 0
那么区间[p,q] , (1<=p<=a+1 , b<=q<=n)是not good的
我们使用map<ll,bool>vis
映射,记录是否存在过pre[i]
代码实现就是如果当前区间中vis[pre[i]]==1了,表示之前存在过pre[i]了
但是这个区间的头和尾都不固定,不好办呀,那我们在算的时候让一端先固定,比如计算头是p尾是q-1位置(q先固定)的区间(p,q]是不是good。
(1) 如果(p,q]区间不是good,那代表对于某个a,p<=a+1满足了,那为了固定q而让这个条件p<=a+1不满足,我们对p进行自增1的操作,直到(p,q]区间是good为止
(2) 如果(p,q]是good,那我们需要计算q结尾,p到q一共有多少个子序列,这些子序列都是满足good的(不然(p,q]则不满足)。满足区间为[p+1,q],[p+2,q]……[q,q],显然有q-p个是满足的。然后q结尾的区间都计算进去了(因为我们的p是从最小依次增长过来的),接下来计算以q+1结尾的区间。
这么一看,这题的思路就是一个尺取法了
时间复杂度O(kn)
AC代码:
#include <bits/stdc++.h>
#define show(x) std::cerr << #x << "=" << x << std::endl;
#define IOS ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
const int MAX = 200500;
#define INF 1e9
const long long MOD = 998244353;
map<ll,bool>vis;
ll aa[MAX];
int main()
{
IOS;
int n;
cin >>n;
for(int i=1;i<=n;++i){
cin >> aa[i];
aa[i]+=aa[i-1]; //记录前缀和数组
}
int l=0,r=1; //表示区间(l,r]
ll ans=0;
vis[0]=1; //如果Sum(l,r]=0 那就不是good区间
while(r<=n){
while(vis[aa[r]]){ //(1)部分,前缀和aa[r]访问了第二次
vis[aa[l]]=0;
l++;
}
vis[aa[r]]=1; //(2)部分,前缀和aa[r]访问过第一次
ans+=r-l; //(2)部分 ,以q为末尾的区间(p,q]有q-p个符合
r++;
}
cout << ans;
return 0;
}