P13546 [OOI 2022] Integral Array 题解
题意简析
若一个可重集合 \(a\) 满足其中任意两个元素 \(x,y\),且 \(x \ge y\),\(\left\lfloor \frac{x}{y} \right\rfloor \in a\),那么称 \(a\) 为整性数组。
现在给你若干个数组,试判断它们是否为整性数组。
思路解析
直接枚举所有数对 \(x\) 和 \(y\) 的时间复杂度为 \(O(n^2)\),对于 \(n \le 10^6\) 不可行的。
注意到题目中给出了值域 \(1 \le c \le 10^7\),因此我们考虑从这方面下手,可以考虑到复杂度和值域相关的桶。
对于数组中出现的每个数 \(y\),以及每个可能的商 \(k\),如果存在一个数 \(x\) 在区间 \([ky, k(y+1) - 1]\) 内,那么 \(k\) 必须存在于数组中。否则,数组不是整性的。
但是我们如何查询呢?我们可以考虑使用前缀和:类似于欧拉筛的思想,对于每个在数组中出现的 \(y\),枚举所有可能的 \(k\)(从 \(1\) 到 \(\left\lfloor \frac{c}{y} \right\rfloor\))。对于每个 \(k\),如果 \(k\) 不在数组中,查询区间 \([k y, \min(k(y+1) - 1, c)]\) 是否有元素存在。若存在,则数组不满足条件。
优化
- 特判:如果数组包含从 \(1\) 到 \(c\) 的所有整数,则直接判定为整性数组。
- 特殊情况:注意到 \(x\) 可以等于 \(y\),那么 \(\frac{x}{x} = 1(x \ne0)\),因此若数组之中没有 \(1\) 肯定不是整性数组。
时间复杂度
调和级数求和:
\[H_n = \sum_{k=1}^n \frac{1}{k}
\]
故时间复杂度为:
\[O(c \ln(c))
\]
代码实现
#pragma G++ optimize("O3", "unroll-loops", "omit-frame-pointer", "inline")
#include <bits/stdc++.h>
using namespace std;
const int MAX_C = 1e7 + 1;
bool has[MAX_C];
int n, c, t, pref[MAX_C];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> t;
while (t--) {
cin >> n >> c;
for (int i = 1; i <= c; i++) {
has[i] = false;
}
for (int i = 0, x; i < n; i++) {
cin >> x;
has[x] = true;
}
for (int i = 1; i <= c; i++) {
pref[i] = pref[i - 1] + has[i];
}
if (pref[c] == c) {
cout << "Yes\n";
continue;
}
bool valid = true;
for (int y = 1; y <= c && valid; y++) {
if (has[y]) {
for (int k = 1; k * y <= c; k++) {
if (!has[k]) {
if (pref[min(k * y + y - 1, c)] - pref[k * y - 1] > 0) {
valid = false;
break;
}
}
}
}
}
if (valid) {
cout << "Yes\n";
} else {
cout << "No\n";
}
}
return 0;
}