**[AHOI 2018初中组] 根式化简 题解 **
好了告诉你们其实这就是一题非常简单的数论……
先考虑最暴力的算法
首先可以明了的一点就是如果我们把一个数 (N) 表示成 (x=k*a^3) 的形式,那么 (k) 一定满足 (k ≤ sqrt[3]{N})。
所以我们可以暴力枚举 (1- sqrt[3]{N}) 的每一个数 (x),只要满足 (x | N) ,我们就判断 (N/x)是否为立方数,这些立方数可以在 (sqrt[3]{N})的时间内预处理出来。
我们看看数据范围,好的你已经 (get) (1-4) 个测试点,也就是 (40) 分了。
有没有优化?
我们注意到 (5,6) 两个特殊数据点:(x) 为立方数,这启示我们可以二分处理掉这一部分数据。
很明显若 (i) 是递增的,那么 (i^3) 也是递增的,所以答案具有单调性。
并且 (sqrt[4]{N} ≤ sqrt[3]{N} ≤ sqrt{N}) ,所以二分下界 $ sqrt{N}$,上界不好取,取 (sqrt[3]{10^{18}}=10^6) 即可。
按照数据打程序,你已经可以通过 (1-6) 个测试点了。(60) 分在没想到正解的时候还是很可观的。
总结启示,得到正解
你知道 (N=k*a^3) ,那么应该会有:(k,a≤sqrt[3]{N}) 对吧?那么我们肯定不能暴力对 (N) 质因数分解,所以我们想想这些因数在什么范围内才是我们所要的?最简单的上界也是很容易想到的:(sqrt[3]{N})。看一眼数据,唔!又过了两个点(QAQ)。
单着并不是我们要的,我们在期望正解。很显然 (sqrt[3]{N}) 这个上界还是太大,因此我们需要观察还有没有更小的上界。其实我们发现可以把上界缩小到 (sqrt[4]{N}),然后$ forall i∈[1,sqrt[4]{N}], ext{且}i∈prime,imid N $,把 (N) 中 (i) 全部除去,并且只要除的次数每逢 (3) 的倍数就把 (ans) 累乘上 (i)。
为什么这样可行?这么做极有可能剩下一个较大的 (k) 还可以继续分解啊?
其实是不可能的,如果剩下除完了的 (N) 还能继续分解成上面那种形式的话,那么它早就被 ([2,sqrt[4]{N}]) 内的质因子除掉了,除非剩下了一个立方数。
所以我们再次利用二分判断剩下的除完的那个 (N) 还是不是立方数就好了,如果是累乘上 (sqrt[3]{N})。
接下来根据代码来理解思路吧:
#include<bits/stdc++.h>
using namespace std;
const int N = 4e4 + 10;
const int M = 1e6 + 10;
#define ll long long
int prime[M]; bool v[M];
int Prime(int n) {
int m = 0;
v[0] = v[1] = 1;
for(int i = 2; i <= n; i++) {
if(v[i] == 0) prime[++m] = i;
for(int j = 1; j <= m && prime[j] <= n / i; j++) {
v[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
return m;
}
ll p[M];
int main() {
// 预处理立方根
for(int i = 1; i <= M; i++) p[i] = (ll) i * i * i;
// 预处理质数
int m = Prime(N), T; ll x, ans = 1;
scanf("%d", &T);
while(T--) {
scanf("%lld", &x);
// 在 1-x^(1/4) 内枚举因子
for(int i = 1, cnt = 0; i <= m && p[i] <= x; i++, cnt = 0)
while(x % prime[i] == 0) {
cnt++, x /= prime[i];
if(cnt % 3 == 0) ans *= prime[i];
}
// 特判剩下的数
ll k = lower_bound(p + 1, p + M + 1, x) - p;
if(k * k * k == x) ans *= k;
printf("%lld
", ans);
ans = 1;
}
return 0;
}