CF2067E

题目链接

题意

对于一个长度为 n 的正整数序列 a,定义它为「有魔力的」,当且仅当:1i<nmin(a1,...,ai)mex(ai+1,...,an)

现在有多组询问,每次给定一个正整数 n 和一个长度为 n 的序列 a,问 a 的最长「有魔力的」子序列有多长。

数据保证 1n2×1050ai109

分析

被这题打爆了呜呜,可能写不出一些逻辑性的思考了。

首先我们有一个观察是:对于序列 a,如果这个序列中不存在 0,那么它必定是「有魔力的」。这个的证明比较显然:此时 1i<nmex(ai+1,...,an)=0,而 min(a1,...,ai) 必定是自然数,故该观察成立。

进一步地,若序列中有两个及以上的 0 存在,那么它必定不是「有魔力的」。证明只需要取两个 0 中间的一个位置作为 i 即可。

我们发现这两个观察,可以对原序列的最长「有魔力的」子序列进行一个很大的限制。具体地,设原序列中 0 的个数为 cnt,则:

  1. cnt=0,则答案为 n

  2. 否则,答案为 ncntncnt+1

接下来只需要讨论第二种情况即可。一个显然的想法是枚举 a 的所有不为 0 的元素与一个 0 组成的子序列并依次判断,复杂度为 O(n2),显然过不了。并且每次判断都需要处理前缀 min 和后缀 mex,并不是很好快速转移。

但是还有一个性质是:对于一个不含 0 的序列,若在下标为 i 的位置插入一个 0 后,序列仍然是「有魔力的」,则在下标为 jj<i)的地方插入一个 0,序列必定也是「有魔力的」。证明只需考虑在 j 插入 0 后所有可能不合法的位置必定在 i 的方案中出现过即可。

于是在原序列 a 中存在 0 时,我们只需要检查 a 的所有不为 0 的值加上 a 中最左边的 0 组成的序列是否是「有魔力的」即可。

  • 若它是有魔力的,则答案为 ncnt+1

  • 否则,答案为 ncnt

这个检查需要统计前缀 min 和后缀 mex,而这两个东西的 O(n) 求法都是简单的。

#include <bits/stdc++.h>
using namespace std;
#define ll long long

const int N = 2e5 + 5;

int T, n;
int a[N], buc[N], pre[N], suf[N];

void Solve() {
	cin >> n; int cnt = 0;
	for (int i = 1; i <= n; ++i) { cin >> a[i]; cnt += (a[i] == 0); }
	if (cnt == 0) { cout << n << endl; }
	else {
		int m = 0, res = 0; bool flag = true;
		for (int i = 1; i <= n; ++i) {
			if (a[i] != 0) { a[++m] = a[i]; }
			else if (flag) { a[++m] = a[i]; flag = false; }
		}
		for (int i = 1; i <= m; ++i) pre[i] = (i == 1 ? a[i] : min(pre[i - 1], a[i]));
		for (int i = m; i > 0; --i) {
			if (a[i] <= m) ++buc[a[i]];
			while (buc[res]) ++res;
			suf[i] = res;
		}
		for (int i = 1; i <= m; ++i) if (a[i] <= m) --buc[a[i]]; // clear
		for (int i = 1; i < m; ++i) if (pre[i] < suf[i + 1]) { cout << n - cnt << endl; return ; }
		cout << n - cnt + 1 << endl;
	}
}

signed main() {
	cin >> T;
	while (T--) Solve();
	return 0;
}
posted @   zyb_txdy  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示