• [AHOI 2018初中组] 根式化简 题解


    **[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;
    }
    
  • 相关阅读:
    20162302
    20162302
    20162302 实验三《敏捷开发与XP实践》实验报告
    20162302 第九周作业
    20162302 第八周作业
    20162302 实验二《面向对象程序设计》实验报告
    20162302 第七周作业
    项目Alpha冲刺Day8
    项目Alpha冲刺Day7
    项目Alpha冲刺Day5
  • 原文地址:https://www.cnblogs.com/Ning-H/p/11581108.html
Copyright © 2020-2023  润新知