codeforces 1333C - Eugene and an array (贡献法,前缀和,双指针)

题目大意:

我们称一个子串是好的,当且仅当里面的连续子串不能求和为0。 现在给我们一个串an,问我们里面有多少个好的连续子串。

len(an)<=1e5.

解题思路:

首先,我们要用一种贡献法的思维。更具体地,我们发现,假如在an中部分区间[l,r]求和为0,以[l,r]为基础的向外拓张的串都是不好的串,例如[l-1,r] , [l,r+1]等等这种,所以我们的好的串必定不能包含[l,r]。 [l,r]贡献出了不好的串。

那么我们怎么快速找到[l,r]之间的数求和为0呢。很明显,我们可以使用前缀和结构,令前缀和为pre[n],那么pre[r] == pre[l-1] 就代表[l,r]中的数求和为0. 那么到这里,我们就知道我们需要在前缀和中找到含有相等的数的区间[l,r],l左端点对答案的贡献为:r - l - 1.

那么现在有一个n^2复杂度的算法,我们需要继续优化。我们发现,再找到一对相同的数字时,右指针是不需要重新归位的。毕竟我们前面已经走过了,证明那一段区间是不包含重复数的。这时候我们只需要让左指针往前走即可,直到我们把重复的数字去掉,然后再去走右指针。

双指针在走的时候,我们同时也要考虑是用右指针作贡献,还是左指针作贡献。

类似的双指针题目可以看

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

#include <bits/stdc++.h>
#define int long long 
using namespace std;
int32_t main(){
    int n;cin>>n;
    map<int,int> mm;
    vector<int> pre(n+2,0);
    for(int i=1;i<=n;i++){
        cin>>pre[i];
        pre[i]+=pre[i-1];
    }
    int i = 0 ,j = 1;
    map<int ,int> ms;
    ms[0]=0;
    int ans=0;
    while(i<= n && j<=n){
        if(!ms.count(pre[j])){
            ans += (j - i  ) ;
            ms[pre[j]] = j;;
            j++;
        }else{
            ms.erase(pre[i]);
            i++;
        }
    }

    cout<<ans<<endl;
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值