Codeforces 1333C. Eugene and an array(思维) /详解

Codeforces Round #632 (Div. 2) C. Eugene and an array

题意:
求出一个数列中子区间满足 此区间的任意子区间之和 不为0的区间个数。

思路:

  1. 考虑用 d p [ x ] dp[x] dp[x]记录前缀和为 x x x的区间右端点。
  2. 那么这道题其实可以看成用map记录前缀和的路径,依次计算每个元素作为区间右端点并且满足条件时对答案的贡献,再进行累加即可。
  3. i i i是以 a [ i ] a[i] a[i]为右端点的子区间个数, l a s t last last是 距离 i i i最近且 [ l a s t , i ] [last,i] [last,i]中包含和为0的子段的端点,那么即说明 [ l a s t + 1 , i ] [last+1,i] [last+1,i]不包含和为0的子区间,所以每次遍历对答案的贡献为 i − l a s t i-last ilast
  4. 那么怎么求 l a s t last last?
  5. d p [ x ] dp[x] dp[x]在前面出现过的时候就说明区间 [ d p [ x ] + 1 , i ] [dp[x]+1,i] [dp[x]+1,i]之间的子段和为0,那么很明显,此时 l a s t = m a x ( l a s t , d p [ x ] + 1 ) last=max(last,dp[x]+1) last=max(last,dp[x]+1)
  6. 这里取 m a x max max是防止 d p [ x ] + 1 dp[x]+1 dp[x]+1 l a s t last last小,而导致容纳了一些错误的区间。
  7. 例如 [ d p [ x ] + 1 , i ] 和 [ l a s t , i ] [dp[x]+1,i]和[last,i] [dp[x]+1,i][last,i]中均包含了子段和为0的区间,但是如果 l a s t last last d p [ x ] + 1 dp[x]+1 dp[x]+1的话就会把原先的 [ l a s t , i ] [last,i] [last,i]之间的错误区间算进答案中,所以上面才会提及 l a s t last last是 距离 i i i 最近的一点。
  8. 但是不仅仅是如此,还要注意初始化 : d p [ 0 ] = l a s t = 0 dp[0]=last=0 dp[0]=last=0,其他应该没什么问题和坑点了。
  9. 如果觉得有帮助的话给个赞,谢谢!
  10. 或者有什么讲的不清楚的欢迎留言私信交流~

代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define inf 0x3f3f3f3f
#define mod 1000000007
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))
using namespace std;
inline ll read(){ll s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();return s*w;}
void put1(){ puts("YES") ;}void put2(){ puts("NO") ;}void put3(){ puts("-1"); }
ll qp(ll a,ll b, ll p){ll ans = 1;while(b){if(b&1){ans = (ans*a)%p;--b;}a =
(a*a)%p;b >>= 1;}return ans%p;}
const ull base=2333; const ull pp=19260811; const ull ppp=999998639;
 
const int manx=1e5+5;
const int mo=998244353;
 
ll s[manx];
map<ll,ll>dp;
 
int main(){
    ll n=read();
    ll ans=0,s=0,last=0; dp[s]=0;
    for(int i=1;i<=n;i++){
        ll x=read();
        s+=x;
        if(dp.count(s)) last=max(last,dp[s]+1);
        dp[s]=i;
        ans+=i-last;
     //   cout<<ans<<" "<<last<<endl;
    }
    cout<<ans;
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值