C. Eugene and an array (详细讲解)

本文详细解析了C. Eugene和数组的问题,重点在于理解什么是good的数组,即不包含和为0的子串。通过枚举数组元素并维护和为0的区间最大左端点,使用前缀和技巧求解。文章提供了清晰的思路及AC代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


C. Eugene and an array

  • 看我博客我有没看懂的地方,或者其他疑问,可以加我qq和我交流~我会及时解答
  • qq:1244536605 (加好友时备注一下 博客 )

标签

  • 难想的暴力。

简明题意

  • D题没啥思路,放弃比赛直接开始写博客哈哈哈哈~
  • 给定n长的数组a[]
  • (接下来的文章中,我把连续子序列称为子串,大家注意一下哈)
  • 题目定义了一个数组什么时候是good的。题目是这样定义的:一个数组b[],如果它是由a[]数组删掉头部几个元素,再从尾部删掉几个元素得到的,且b[]数组不包含一个和为0的子串,那么b[]是good的。
  • 题目定义得很绕。。什么头部删几个,尾部删几个,,这都是误导的信息。实际上就是说,如果一个数组是good的,那么这个数组需要是a[]的一个子串,且这个数组本身不包含和为0的子串。题目中,头部删几个,尾部删几个,我开始还想了半天这是啥…后来才明白其实就是告诉你它是a[]的一个子串。
  • 现在要求a[]有多少个子串是good的。

思路

  • 包含a[i]的子串一共有多少个?
  • 显然是n个。从a[i]往左边延伸得到i-1个,从a[i]往右延伸是n-i个,加上自己,就是n个。
  • 那么我们可以枚举每一个a[i],求a[i]往左边可以延伸出多少个good的子串,再把对于每个a[i]求得的值加起来即可。
  • 下面画一个图,蓝色部分表示我们正在考虑的a[i]。假设红色和棕色标出的区间和为0.相当于现在我们固定了子串的右端点,想要求出有多少个左端点可选,使得这个子串good。
    在这里插入图片描述
  • 那么很显然,左端点不可能在 L 3 L_3 L3的前面。那么是不是相当于我们在枚举a[i]的时候要维护,和为0的区间的最大左端点?是的。
  • 假设我们维护的这个最大左端点是max_l,那么对于每一个a[i],我们让ans += i-max_l就好了。
  • 嗯…我好像讲完了,就是这么简单,维护就好了。

---------------------------------------分割线-----------------------------------

  • 可能难点在于怎么维护。我说说我的思路。我一看到区间和为0,我想到了前缀和,区间 [ i , j ] [i,j] [i,j]和为0等价于 s u m [ i − 1 ] = s u m [ j ] sum[i-1]=sum[j] sum[i1]=sum[j]。那么我们可以先前缀和一下,然后考虑每一个a[i]的时候,直接找距离i最近的j且sum[j]=sum[i]。就能找到这个和为0的区间了,然后用这个更新max_l,再用max_l更新答案即可。

注意事项


总结


AC代码

#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring> 
#include<stack>
#include<map>
#include<queue>
#include<cstdio>	
#include<set>
#include<map>
#include<string>
using namespace std;

const int maxn = 2e5 + 10;

long long dp[maxn];

map<long long, int> rec;//维护每个前缀和最近一次出现的位置
void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> dp[i], dp[i] += dp[i - 1];
		rec[dp[i]] = -1;
	}

	long long ans = 0;
	int max_l = -1;
	rec[0] = 0;
	for (int i = 1; i <= n; i++)
	{
		if (rec[dp[i]] != -1) 
			max_l = max(max_l, rec[dp[i]]);
		rec[dp[i]] = i;
		ans += i - (max_l + 1);//max_l表示前缀和相等的下标,而max_l+1才是实际的区间
	}
	
	cout << ans;
}

int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值