CF1333C Eugene and an array

提供一种复杂度为 O(nlog2n)O(n \log^2 n) 的做法。

考虑数据结构加二分。

如果我们指定某个区间的 ll,那么你可以发现的是,区间是否存在连续子序列和为 00 是单调性的,即如果 r=xr = x 时,有区间和为 00,那么对于 r>xr>x,也一定有。

所以可以考虑枚举 ll,二分 rr。接下来考虑如何判定区间是否有和为 00

可以将原序列做一次前缀和,那么如果一个区间内有和为 00,意味着区间内有一个数本身为 00,或者其前缀和中有两个或以上相同的数。判断是否有两个或以上相同的可以考虑莫队等数据结构维护,但是是离线的,而二分必须在线。考虑设 sumi=j=1iajsum_i = \sum \limits_{j=1}^i a_j,即前缀和,设 preipre_imsub(sumi=sumj)j_{\max}(sum_i=sum_j),询问区间 l,rl, r,相当于询问区间的 prepre 的最大值。考虑 st 表预处理。询问是否有 00 本身,即询问区间内某个数的出现次数,考虑 vector 上二分可以在线处理。

#pragma GCC optimize("-Ofast")
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
#include <cstring>
using namespace std;

#define int long long

const int N = 2e5 + 5, M = 25;

int a[N], n, pre[N], p[N], LG2[N], s2[N];
vector<int> b;

map<int, vector<int> > pee;

void Init()
{
	LG2[1] = 0;
	LG2[2] = 1;
	for (int i = 3; i < N; i++)
	{
		LG2[i] = LG2[i / 2] + 1;
	}
}

int f[N][M];

inline int query(int l, int r)
{
	int x = LG2[r - l + 1];
	return max(f[l][x], f[r - (1LL << x) + 1][x]);
}

signed main()
{
	Init();
	scanf("%lld", &n);
	int ret = n * (n - 1) / 2 + n;
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &a[i]);
		if (a[i] == 0) ret--;
		a[i] += a[i - 1];
		s2[i] = a[i];
		b.push_back(a[i]);
		pee[a[i]].push_back(i);
	}
	sort(b.begin(), b.end());
	b.erase(unique(b.begin(), b.end()), b.end());
	for (int i = 1; i <= n; i++) a[i] = lower_bound(b.begin(), b.end(), a[i]) - b.begin() + 1;
	for (int i = 1; i <= n; i++)
	{
		pre[i] = p[a[i]];
		p[a[i]] = i;
	}
	for (int i = 1; i <= n; i++)
	{
		f[i][0] = pre[i];
	}
	for (int j = 1; j < M; j++)
	{
		for (int i = 1; i + (1ll << j) - 1 <= n; i++)
		{
			f[i][j] = max(f[i][j - 1], f[i + (1ll << (j - 1))][j - 1]);
		}
	}
	for (int i = 1; i < n; i++)
	{
		int l = i, r = n + 1, place = -1;
		while (l + 1 < r)
		{
			int mid = l + r >> 1;
			int x = query(i, mid);
			if ((x >= i - 1 && x != 0) || upper_bound(pee[-s2[i - 1]].begin(), pee[-s2[i - 1]].end(), mid) - lower_bound(pee[-s2[i - 1]].begin(), pee[-s2[i - 1]].end(), i) >= 1) r = mid, place = mid;
			else l = mid;
		}
		//printf("%lld %lld %lld\n", i, place, query(i, place));
		if (place != -1) ret -= (n - place + 1);
	}
	printf("%lld\n", ret);
	return 0;
}
posted @   HappyBobb  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示