对 \(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\) 的大小关系。
-
\(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\) 个数。 -
\(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;
}
标签:Subset,Atcoder,insert,int,Sum,对于,个数,long,操作
From: https://www.cnblogs.com/lhzawa/p/17554961.html