立方数
题目描述
对于给定的正整数 $N$,求最大的正整数 $A$,使得存在正整数 $B$,满足 $A^3B=N$。
输入包含 $T$ 组数据,$1 \leq T \leq 10000$;$1 \leq N \leq 10^{18}$。
输入描述:
第一行数字 $T$ 表示数据组数。
接下来一行,$T$ 个正整数 $N$。
输出描述:
$T$ 行,每行一个数字表示答案。
示例1
输入
4
27 24 7 54
输出
3
2
1
3
解题思路
感觉这题的正解挺难想到的,也很难解释为什么会这么想,我尽量构造一下其中的逻辑链。
将 $N$ 分解成质因子乘积的形式 $N = P_1^{\alpha_1} P_2^{\alpha_2} \cdots P_m^{\alpha_m}$,那么最大 $A$ 就是 $P_1^{\left\lfloor \alpha_1/3 \right\rfloor} P_2^{\left\lfloor \alpha_2/3 \right\rfloor} \cdots P_m^{\left\lfloor \alpha_m/3 \right\rfloor}$。因此只有幂次至少为 $3$ 的质因子才对 $A$ 有贡献。但问题在于,我们得先知道 $N$ 的质因子分解,才能计算出这些幂次,而要枚举 $N$ 以内的质数显然不可行。
我们可以先从枚举约数的方法开始,把 $A^3 B = N$ 中的 $A^3$ 与 $B$ 看作是 $N$ 的约数。我们可以枚举 $N$ 的所有约数 $d$,如果 $\sqrt[3]{d}$ 是整数,则说明存在一个满足条件的 $A = \sqrt[3]{d}$。我们只需要找到所有约数中 $\sqrt[3]{d}$ 为整数的最大那个 $d$,其对应的 $\sqrt[3]{d}$ 就是最大的 $A$。但朴素枚举 $N$ 的所有约数的复杂度为 $O\left( \sqrt{N} \right)$,会超时。
接着再想办法优化,注意到 $A^3 B = N \Rightarrow A = \sqrt[3]{N/B} \leq \sqrt[3]{10^{18}/1} = 10^6$,这说明我们其实只需要考虑 $10^6$ 以内的质因子,因为如果某个质因子大于 $10^6$,其 $3$ 次幂就会超过 $10^{18}$,与 $N \leq 10^{18}$ 矛盾。所以我们可以先筛出 $10^6$ 以内的所有质数(一共 $78498$ 个质数),然后依次枚举这些质数来试除 $N$,求出每个质因子的幂次,再套用最开始提到的公式计算出最大 $A$。然而处理单个 $N$ 的复杂度为 $\pi\left(\sqrt[3]{N}\right)$,所有询问的总复杂度为 $T \cdot \pi\left(\sqrt[3]{N}\right)$,还是会超时。
实际上做到这里我也很难再想到更优的解法了,无奈下只能查看题解,结果发现只需考虑 $\sqrt[4]{N}$ 内的质因子就行。这个步骤的跳跃性很大,难以解释为什么。不过我们还是可以硬推一下其背后的逻辑。首先尝试 $\sqrt{N}$ 内的约数,发现不行再考虑 $\sqrt[3]{N}$ 内的质因子,还不行再试试 $\sqrt[4]{N}$ 的质因子。下面来分析一下为什么只需考虑 $\sqrt[4]{N}$ 的质因子。
我们先枚举 $\sqrt[4]{N}$ 以内的质因子并试除 $N$,剩下的部分记为 $N'$,将其分解成 $N' = P_1^{\alpha_1} P_2^{\alpha_2} \cdots P_m^{\alpha_m}$,其中每个质因子都大于 $\sqrt[4]{N}$ 的,即 $P_i > \sqrt[4]{N}$。此时可以分两种情况讨论:
- 如果 $m=1$,即 $N' = P_1^{\alpha_1}$ 只有一个质因子,那么一定有 $\alpha_1 \leq 3$。否则如果 $\alpha_1 \geq 4$ 则 $P_1^{\alpha_1} > \left(\sqrt[4]{N}\right)^4 > N$,矛盾。
- 如果 $m > 1$,则每个 $\alpha_i < 3$。比如某个 $\alpha_i \geq 3$,有 $P_i^{\alpha_i} P_j^{\alpha_j} > \left(\sqrt[4]{N}\right)^3 \times \sqrt[4]{N} = N$,矛盾。
此时,我们只需 $O(1)$ 判断 $\sqrt[3]{N'}$ 是否为整数,就可以确定 $N'$ 是否存在幂次为 $3$ 的唯一质因子。最终总的复杂度为 $T \cdot \pi\left(\sqrt[4]{N}\right)$,可以通过。
AC 代码如下,时间复杂度为 $O\left(T \cdot \pi\left(\sqrt[4]{N}\right)\right)$:
#include <bits/stdc++.h>
using namespace std;typedef long long LL;const int N = 32005;int prime[N], cnt;
bool vis[N];void init() {for (int i = 2; i < N; i++) {if (!vis[i]) prime[cnt++] = i;for (int j = 0; prime[j] * i < N; j++) {vis[prime[j] * i] = true;if (i % prime[j] == 0) break;}}
}void solve() {LL n;cin >> n;LL ret = 1;for (int i = 0; i < cnt; i++) {int p = prime[i], c = 0;while (n % p == 0) {c++;n /= p;}c /= 3;while (c--) {ret *= p;}}LL t = cbrtl(n);if (t * t * t == n) ret *= t;cout << ret << '\n';
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);init();int t;cin >> t;while (t--) {solve();}return 0;
}
参考资料
【题解】2020牛客寒假算法基础集训营第六场:https://ac.nowcoder.com/discuss/367149