Atcoder AGC062C Mex of Subset Sum

发布时间 2023-07-14 20:55:14作者: lhzawa

\(a_i\) 从小到大进行排序,因为想到若 \(< a_{i - 1}\) 的不能取到的数因为 \(a_i > a_{i - 1}\) 肯定是能保证取不到的。
对排完序的 \(a_i\) 做一个前缀和 \(s_i = \sum\limits_{j = 1}^n\),令 \(A_i\)\(a_{1\sim i}\) 中无法表示为子序列之和且 \(< s_i\) 的正整数的集合。

考虑现在已经处理完 \(i - 1\) 位,对于第 \(i\)\(A_i\) 相对 \(A_{i - 1}\) 有什么变化。
考虑讨论 \(s_{i - 1}\)\(a_i\) 的大小关系。

  1. \(s_{i - 1}\le a_i\)

    • 对于 \(x\in A_{i - 1}\)\(x\),因为 \(a_{i} > s_{i - 1} > x\),所以 \(a_i\) 的存在对 \(x\) 并没有影响。
    • 对于 \(x\in (s_{i - 1}, a_i)\)\(x\),选 \(a_i\) 就会比 \(x\) 大,不选就会比 \(x\) 小,所以能加入 \(A_i\)
    • 对于 \(x - a_i\in A_{i - 1}\)\(x\),肯定 \(x > a_i\),不选 \(a_i\) 则肯定凑不出来,选了在 \(A_{i - 1}\) 中也凑不出来。

    能发现对于第 \(1,2\) 类的 \(x\) 后面的操作肯定不会排除掉,所以若第 \(1,2\) 类的 \(x\) 的数量已经 \(\ge k\) 可以直接输出;但对于第 \(3\) 类的 \(x\) 后面的操作可能会排除一部分,其对答案的贡献是不确定的,不能直接输出。

    时间复杂度分析:
    考虑第 \(1, 2\) 类操作因为个数 \(\ge k\) 就直接输出,所以个数一定 \(< k\),第 \(3\) 类操作个数上限就为 \(|A_{i - 1}|\),所以有 \(|A_i| < |A_{i - 1}| + k\),即每次操作 \(A_i\) 内最多增加 \(k\) 个数。

  2. \(s_{i - 1} > a_i\)

    • 对于 \(x\in A_{i - 1}\cap [1, a_i)\)\(x\),因为 \(a_i > x\),所以 \(a_i\) 的存在对 \(x\) 并无影响。
    • 对于 \(x\in A_{i - 1}\cap (a_i, s_{i - 1})\)\(x - a_i \in A_{i - 1}\)\(x\),选不选 \(a_i\) 都凑不出来。
    • 对于 \(x\in (s_i, \infty)\)\(x - a_i\in A_{i - 1}\)\(x\),不选 \(a_i\) 肯定小,选了 \(a_i\) 也凑不出来。

    能发现对于第 \(1\) 类的 \(x\) 后面的操作肯定不会排除掉,所以若第 \(1\) 类操作的 \(x\) 的数量已经 \(\ge k\) 可以直接输出;但对于第 \(2,3\) 类的 \(x\) 后面的操作可能会排除一部分,其对答案的贡献是不确定的,不能直接输出。

    时间复杂度分析:
    考虑第 \(1\) 类操作因为个数 \(\ge k\) 就直接输出,所以个数一定 \(< k\),第 \(2, 3\) 类操作个数上限就为 \(|A_{i - 1}|\),所以有 \(|A_i| < |A_{i - 1}| + k\),即每次操作 \(A_i\) 内最多增加 \(k\) 个数。

若跑完后 \(|A_n| < k\),直接从 \(s_n + 1\) 开始往后选数直到 \(|A_n| = k\)

考虑用 set 来维护集合,时间复杂度为 \(O(n^2k\log_2 (nk))\)

// lhzawa(https://www.cnblogs.com/lhzawa/)
// Problem: C - Mex of Subset Sum
// Contest: AtCoder - AtCoder Grand Contest 062
// URL: https://atcoder.jp/contests/agc062/tasks/agc062_c
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int N = 60 + 5;
long long a[N];
int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
	}
	function<void (const set<long long>&)> print = [&k](const set<long long> zxx) -> void {
		for (set<long long>::iterator i = zxx.begin(); k; i++, k--) {
			printf("%lld ", (*i));
		}
		return ;
	};
	sort(a + 1, a + n + 1);
	long long s = 0;
	set<long long> A, B;
	for (int i = 1; i <= n; i++) {
		if (s <= a[i]) {
			B = A;
			for (long long j = s + 1; j < a[i]; j++) {
				A.insert(j);
				if (A.size() >= k) {
					print(A);
					return 0;
				}
			}
			for (long long j : B) {
				A.insert(j + a[i]);
			}
		}
		else {
			B.clear();
			for (long long j : A) {
				if (j < a[i]) {
					B.insert(j);
				}
				if (B.size() >= k) {
					print(B);
					return 0;
				}
			}
			for (long long j : A) {
				if (a[i] < j && A.count(j - a[i])) {
					B.insert(j);
				}
				if (j + a[i] > s) {
					B.insert(j + a[i]);
				}
			}
			A = B;
		}
		s += a[i];
	} 
	for (s++; A.size() < k; A.insert(s), s++);
	print(A);
	return 0;
}