D. Non-zero Segments(前缀和+思维)Codeforces Round #674 (Div. 3)

原题链接: https://codeforces.com/contest/1426/problem/D

在这里插入图片描述
测试样例

input
4
1 -5 3 2
output
1
input
5
4 -2 3 -9 2
output
0
input
9
-1 1 -1 1 -1 1 1 -1 -1
output
6
input
8
16 -5 -11 -15 10 5 4 -4
output
3

Note

Consider the first example. There is only one subsegment with the sum 0. It starts in the second element and ends in the fourth element. It’s enough to insert one element so the array doesn’t contain any subsegments with the sum equal to zero. For example, it is possible to insert the integer 1 between second and third elements of the array.

There are no subsegments having sum 0 in the second example so you don’t need to do anything.

题意: 给你一个数组 a a a,现在你不想使得数组中的任意子序列和为 0 0 0,你可以在任意相邻对中添加任意大小的数使得达成目的。那么你需要进行的最小添加次数使得要求成立。

解题思路: 很明显的前缀和问题。为什么呢?因为我们探寻的是子序列,而子序列类型是有很多的,如果我们用前缀和自然会省去很多麻烦。那么我们接下来来深究这个问题,我们如果发现某个子序列为 0 0 0之后我们进行添加操作这个会使得前面的+后面的数不可能为 0 0 0了,因为我可以加个无穷大或者负无穷,这自然可以达成目的,所以这里是可以跳过的。 所以我们可以在线统计前缀和,通过 m a p map map记录前缀和是否出现过,如果出现同样的前缀和,那么这两个前缀和的差集自然是为 0 0 0的。所以这里我们进行操作之后这个前缀里是不可能再对后面的进行影响了,所以我们可以更新累加值。同时也要清空 m a p map map容器避免对之后的造成影响。(我可能说得有点乱,有问题评论区留言,我都会给予解答,具体看代码。)

AC代码

/*
*邮箱:unique_powerhouse@qq.com
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 2e5+4;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

ll t,n;
ll a[maxn];
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>n){
		rep(i,1,n){
			cin>>a[i];
		}
		map<ll,bool> p;
		ll cnt=0,sum=0;
		//要清楚的是,如果前缀和为0,自然就是要处理的,故我们在这里对0进行标记。
		p[0]=true;
		rep(i,1,n){
			sum+=a[i];
			if(p.count(sum)){
				//判断前缀和在之前是否存在,如果存在这里的前缀和减去之前的前缀和自然会导致中间为0,那么此刻这里就需要添加。
				cnt++;
				//那么之前的记录都清除,因为我们已经添加改变。
				p.clear();
				//前缀和从此刻开始算起。
				sum=a[i];
				//同样是记录。
				p[sum]=true;
				p[0]=true;
			}
			else{
				//同样进行标记。
				p[sum]=true;
			}
		}
		cout<<cnt<<endl;
	}
	return 0;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页