2025-12-15题目:Codeforces Round 1068 (Div. 2) C
题源
https://codeforces.com/contest/2173/problem/C
题意
Kanade在研究一个数字游戏。首先有两个数字n与k,以及具有n个整数的数组a,且该数组满足条件1≤ai≤k。你需要找到一种“完全数组”B={b1,b2,…,bm},不仅也满足1≤bi≤k,还要满足下面两个条件:
- 对于1≤i≤n(即对于数组a中的每一个),至少ai有一个因数在B当中。
- 对于1≤j≤m(即对于数组B中的每一个),所有bj的正倍数(在k及以内的)至少在数组a出现一次。
你要找到满足这种条件的最小的B数组,或者说明不存在这种数组。
其他条件包括1≤m≤n,1≤n≤2⋅105,1≤k≤109,限时2秒。
提示
- 举个简单的例子解释题意,若有k=6,a={2,3,4,6},那么B={2,3}。a中每一个数字都至少有一个因数在B中,且B中所有元素在6及以下的倍数都在a中。
- 观察假设当a数组只有1个或2个元素时,B数组有什么规律。
- 思考a数组中有没有什么B数组必定包含的元素。
题解
对于a中最小的元素x,由于x已是最小,因此没有x的因数在a中。如果它不在B中,就违反了规则1。
因此x必定在B中。既然如此,根据规则2,不难知道a里面还应该出现x直到k的倍数,否则不存在这样的B。我们因此可以反过来验证a是否满足条件,并将这些x的倍数打标记。
验证完x后,a中未被打标记的都不是x的倍数。对于这些数,我们可以进行相同的操作——找到最小一个、反向验证。注意同一个数可以被多次标记。
对于C++中如何实现上述思路,我们可以使用STL中的set来表示a,标记方式为将其从a中删除,我们无需对a排序,因为set本来就是自动排序的。而是否出现过可以使用map来表示。总的时间复杂度大约为O(nlog(n))。
部分代码
set<int> a;
map<int, int> p;
vector<int> ans;
for (int i = 0; i < n; i ++) {
int tmp;
cin >> tmp;
a.insert(tmp);
p[tmp]++;
}
while (a.size()) {
int t = *a.begin();
ans.push_back(t);
for (int cur = t; cur <= k; cur+=t) {
if (!p[cur]) {
cout << "-1\n";
return;
}
auto t = a.find(cur);
if (t != a.end()) a.erase(t);
}
}