1 条题解
-
0
思路与算法
在所有 的整数中,先最大化约数个数,再在并列时取最小 。这个最小数的约数数严格超过它前面的每个数,故它是反素数;之后直到 不可能再刷新纪录,因此它就是不超过 的最大反素数。
若 ,约数数为 。取得某个约数数的最小整数,使用的素数一定从2连续开始,否则替换成更小的缺失素数会减小整数而不改约数数。指数必须非增:小素数指数比大素数小,交换两指数会使整数更小,约数数仍相同。
据此按素数 深搜,状态记录素数位置、允许的最大指数、当前数值和约数个数。先用当前状态更新最优答案,再尝试当前素数的正指数,下一层指数上限设为本层指数。若再乘一次会超过 ,则终止本层后续尝试。前10个素数乘积 ,所以合法反素数至多9种不同素因子。首层指数至多30。
正确性说明
选择最大约数数并在并列时取最小整数的规则给出一个纪录数;若它之后还存在不超过 的纪录数,后者约数数必须更大,与最大性矛盾。所以该规则与目标等价。
缺失素数替换及指数交换证明表明,某个全局最优解一定由连续素数、非增正指数组成,深搜不会因这些限制遗漏最优解。每层枚举所有不超过上一层的指数;对当前数值先更新答案,也覆盖使用更少素数的解,包括1。若当前数值乘素数已超过上界,更高指数及任何继续扩展都不可能合法,因此剪枝安全。所有合法标准形都被遍历,维护的并列最小规则最终选出所求反素数。
复杂度
设实际访问的合法前缀状态数为 。每条递归边只生成一次,每层结束最多再做一次失败检查,总时间 ;一个宽松界为 ,非增指数限制使实际状态远少于此界。质数表固定,初始化为常数;递归至多9层,额外空间在本题范围为 。不分配长度为 的数组。
易错点
- 并列约数数时必须取较小数;较大的并列数没有严格刷新纪录。
- 1本身是反素数,初始最优值和约数数均为1。
- 非增指数限制依赖最小整数证明,不能跳过小素数后任意继续搜索。
- 先用 value > n/p 判断再乘,避免溢出。递归参数传值,不让不同分支的数值和约数数互相污染。
- 数据包含纪录刚好到达及前一个数、最小值和最大 。独立答案枚举全部164706个9素数平滑数,不使用非增指数剪枝,并用1至50000的全量约数筛核对。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; using ll = long long; const int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; ll limit, best = 1; int bestCount = 1; void dfsAntiprime(int at, int maxExponent, ll value, int count) { if (count > bestCount || (count == bestCount && value < best)) { bestCount = count; best = value; } if (at == 9) return; ll next = value; for (int exponent = 1; exponent <= maxExponent; exponent++) { if (next > limit / primes[at]) break; next *= primes[at]; dfsAntiprime(at + 1, exponent, next, count * (exponent + 1)); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> limit; dfsAntiprime(0, 30, 1, 1); cout << best << '\n'; }
- 1