Codeforces Round #632 (Div. 2) C. Eugene and an array
题意:定义一个非空序列,如果他的所有连续子序列的序列和都不为0,那么就成这个序列是个好序列。然后现在给个序列a,问这个序列a的所有连续子序列中好序列的数量。
Examples
input
3 1 2 -3
output
5
input
3 41 -41 41
output
3
原题链接:http://codeforces.com/contest/1333/problem/C
赛中就知道是个线性dp+二分定位存在后缀和为0的子序列最大下标,但是当时没想明白怎么快速找到维护后缀和为0的情况QAQ,后来看到大佬们可以维护前缀和map,这个操作就把我震撼了(被自己菜哭了)
首先看答案怎么dp
定义dp[i]表示到原序列1~i中有几个好序列,那么dp[i+1] = dp[i] + 以i+1为结尾的好序列的个数
那么问题就在于怎么计算以i+1为结尾的好序列的个数
我们知道对于j到i+1这段(1 <=j <= i+1)如果是好序列,那么j+1~i+1(如果j+1比i+1小的话)一定也是好序列,相反如果j到i+1不是好序列,那么j-1到i+1也不是好序列。所以我们只要把这个好序列跟不好序列的边界找出来就行了。
这里需要注意,以i+1为结尾的序列是坏序列有两种情况,一种是其中不含i+1元素的子序列的和为0导致了整个序列是坏序列,另一种是含i+1元素的子序列的和为0导致了整个序列是坏序列,两者我们都要找出能产生合法序列的最大下标,但是对于前者来讲,如果我们有计算后者的方法,那么计算到i+1时前者就已经知道的了。所以,我们重点在于找以i+1元素为结尾且后缀和为0的所有这样的序列的起始下标,有一种暴力的方法就是从i+1个元素开始往前累加,如果有累加成0的情况就直接记录下来,可是这样会T。
那么除了暴力方法还有什么方法找后缀和为0呢?没错map要登场了。
map记录的是前缀和为x时的最大下标。诶?这有什么用?想想,从1~i+1的和我们是可以知道的,就设为sum[i+1]好了,这时候如果前面也有等于sum[i+1]的前缀和,设为sum[k]好了,sum[k] = sum[i+1],sum[i+1] - sum[k] = 0,那么就说明从k+1到i+1的和为0,这不就是我们要找的所谓后缀和为0的情况么orz。所以我们就把每个前缀和用map把相应最大坐标存下来供以后查找就行了。
AC代码
#include<iostream> #include<algorithm> #include<map> using namespace std; typedef long long ll; const int Maxn = 2e5 + 10; map<ll, ll> f; ll sum[Maxn]; int main() { ll p = -1; f[0] = 0; ll ans = 0; int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> sum[i]; sum[i] += sum[i - 1]; if(f.count(sum[i])) p = max(p, f[sum[i]]); f[sum[i]] = i; ans += i - p - 1; } cout << ans; return 0; }

浙公网安备 33010602011771号