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


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。
    C. Eugene and an array (详细讲解)_第1张图片
  • 那么很显然,左端点不可能在 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
#include
#include
#include 
#include
#include
#include
#include	
#include
#include
#include
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;
}

你可能感兴趣的:(#,计数题,#,难想的暴力)