蒟蒻抢到了 CCF 的题!
考场上打玄学复杂度还没处理 $10^7+1$ 的屑只得了 $70$ 分。
首先,注意到所有包含 7 或者这些数的倍数都不可以报。那么我们可以利用一个类似于埃氏筛的东西来筛出这些不能报的数。
什么是埃筛
埃氏筛法本来是用来找质数的一种质数筛法。
我们知道,一个质数只有 $1$ 和它本身两个因数,所以,我们可以通过任意两个不为 $1$ 的数相乘得到一个合数。
我们开一个数组 $b$,$b_i$ 代表 $i$ 是否为合数。从 $2$ 开始筛,如果此时 $b_i=0$,就说明这个数不能通过任意两个不为 $1$ 的数相乘得到,它是一个质数。
如果 $b_i=1$,说明这是一个合数。我们知道,任何一个数一定有至少一个因子是质数,所以每个数都可以通过让任意一个数和质数相乘得到。用合数再去与别的数相乘会浪费时间,所以我们需要直接跳过下面的步骤。
此时我们拿到了一个质数 $i$,然后我们开始遍历 $2\times i,3\times i,\ldots$,这些全部都是合数,所以把它们全部标记为合数,直到超出筛的范围。
代码很简单,大概长这样:
1 2 3 4 5 6 7 8 9
| void Prime() { for(int i = 2; i * i <= n; i++) { if(f[i]) continue; for(int j = 2 * i; j <= n; j += i) f[j] = 1; } return; }
|
时间复杂度大概是 $O(n\log \log n)$。
怎么做这道题
因为任何含有 $7$ 的数字或其倍数都不可以报出,所以我们可以通过乘积来筛掉所有不合法的数,这很明显是一道埃筛的变形。那么我们该如何做这道题呢?
我们还是开一个 $b$ 数组记录所有的数是否能报,其中 $b_i=0$ 代表 $i$ 可以报,$b_i=1$ 则不能。
首先我们要知道怎么判断一个数 $x$ 是否含有 $7$。这个很简单,只需要重复以下两个步骤:
- 计算 $x/10$ 的余数是否等于 $7$,如果有,说明此数含有 $7$;
- $x$ 除以 $10$。
用这样的方法可以遍历到 $x$ 每个数位上的数。
1 2 3 4 5 6 7 8 9 10 11
| inline bool pan7(int x) { int qwq; while(x) { qwq = x % 10; if(qwq == 7) return 1; x /= 10; } return 0; }
|
每次筛到一个不能报的数,因为其倍数也不能报,所以我们把它乘以不同的数,把这些数也标记为不能报。期间我们需要保证不筛到 $10^7+1$ 外面去。代码长这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| inline void prime() { for(int i = 1; i <= n; i++) { if(pan7(i)) { b[i] = 1; for(int j = 1; j <= n; j++) { int qwq = i * j; if(qwq > n) break; b[qwq] = 1; } } } return; }
|
时间复杂度 $O(n \log n)$。
接着我们已经知道了所有能报的的数,此时我们只需要从大到小遍历 $10^7+1\sim 1$ 的所有数,拿一个变量存当前遍历到的最小能报的数字,每次到一个数,这个变量里存的值就是它报了之后能报的下一个数字,把这个数存在 $ans$ 数组里面。
这样的预处理可以避免查询一个一个跳导致的玄学复杂度。
时间复杂度 $O(n)$。
然后我们就可以实现 $O(1)$ 询问。
一个坑
题目数据范围是 $10^7$,为什么需要筛到 $10^7+1$ 呢?
因为可能询问的就是 $10^7$,而它的下一个数是 $10^7+1$。
这个坑卡掉了许许多多悲伤的 OIers。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include <cstdio> #include <cctype>
const int maxn = (int) 1e7 + 5; const int n = maxn - 4;
int t, q, tot, g[maxn], ans[maxn]; bool b[maxn];
inline bool pan7(int x) { int qwq; while(x) { qwq = x % 10; if(qwq == 7) return 1; x /= 10; } return 0; }
inline void prime() { for(int i = 1; i <= n; i++) { if(pan7(i)) { b[i] = 1; for(int j = 1; j <= n; j++) { int qwq = i * j; if(qwq > n) break; b[qwq] = 1; } } } return; }
inline int read() { int x = 0, w = 0; char ch = 0; while(!isdigit(ch)) { w |= ch == '-'; ch = getchar(); } while(isdigit(ch)) { x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } return w ? -x : x; }
inline void write(int x) { if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar(x % 10 + '0'); }
int main() { prime(); int Ans = (int) 1e7 + 1; for(int i = n; i; i--) { ans[i] = Ans; if(!b[i]) Ans = i; } t = read(); while(t--) { q = read(); write(b[q] ? -1 : ans[q]); putchar('\n'); } return 0; }
|