1 条题解
-
0
思路与算法
从小到大找十进制含7且尚未标坏的整数,把它的全部倍数标坏。对超过最大询问且已找到一个可报数的有限前缀,从后向前建立下一个可报数表,回答坏数-1,否则表中后继。
正确性说明
若某个含7数已被更小含7因子标坏,它的倍数也已被该因子覆盖,所以跳过不会漏标。所有被标数都有含7因子,所有存在含7因子的数都会被某个最早未标因子覆盖。后向扫描保留最近的严格更大好数,查询因而正确。
复杂度
一次筛到B用O(B log B+B log B/ log 10+T)保守时间上界、O(B+T)空间,含标记与后继表初始化;按需扩展B时重新验证存在询问上界后的好数。
易错点
不只是7的倍数,17、27等的倍数也不能报;询问数本身含7直接坏。需越过最大询问找到合法后继,不把数组边界当答案。排版负号‐1按整数-1处理。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct DS { vector<int> p, s; DS(int n) : p(n), s(n, 1) { iota(p.begin(), p.end(), 0); } int find(int u) { while (p[u] != u) { p[u] = p[p[u]]; u = p[u]; } return u; } bool join(int a, int b) { a = find(a); b = find(b); if (a == b) return false; if (s[a] > s[b]) swap(a, b); p[a] = b; s[b] += s[a]; return true; } }; int main() { int T; cin >> T; vector<int> x(T); int mx = 0; for (int &v : x) { cin >> v; mx = max(mx, v); } int N = mx + 1024; vector<unsigned char> bad; auto has7 = [](int x) { while (x) { if (x % 10 == 7) return true; x /= 10; } return false; }; while (true) { bad.assign(N + 1, 0); for (int i = 1; i <= N; i++) if (!bad[i] && has7(i)) for (int j = i; j <= N; j += i) bad[j] = 1; bool extra = false; for (int i = mx + 1; i <= N; i++) if (!bad[i]) { extra = true; break; } if (extra) break; N *= 2; } vector<int> nxt(N + 1, -1); int last = -1; for (int i = N; i >= 1; i--) { nxt[i] = last; if (!bad[i]) last = i; } for (int v : x) cout << (bad[v] ? -1 : nxt[v]) << "\n"; }
- 1