Relatives
ProblemDescription
Given n, a positive integer, how many positive integers less than or equal to n are relatively prime to n ? Two integers a and b are relatively prime if thereare no integers x > 1, y > 0, z > 0 such that a = xy and b = xz.
Input
There are several test cases. For each test case, standard input contains a line with n <= 1,000,000,000. A line containing 0 follows the last case.
Output
For each test case there should be single line of output answering the question posed above.
Sample Input
7
12
0
Sample Output
6
4
刚开始想法是先用线性筛素数伐打一个素数表和利用公式求欧拉函数,没想到刚开始因为估计素数个数不是太多,开的数组太小,导致内存引用无效,后来加大数组,还是tle了,然后参看了一下大神的blog开始改用质因数分解定理,边分解质因数,边约分的策略,但是由于细节处理不当还是tle了,最后看看了大神的细节处理,又改写了一遍代码,最终ac了。
#include <stdio.h> int ans; void Eu(int n){ for(int i = 2; i * i <= n;++i){ if(!(n%i)){ ans *= i - 1; n /= i; while(!(n%i)){ ans *= i;n/=i; } } } if(n > 1){ ans *= n -1; } return ; } int main(){ int n; while(scanf("%d",&n),n){ ans = 1; Eu(n); printf("%d\n",ans); } return 0; }
没处理好的部分是: i <= n这一部分。和 i*i <=n的最大优点是在处理大质数的地方可以省很多的的时间,比如17上面的循环只需执行5次即可,而下面的执行17次。而在每一次的求质因数的时候,都能体现这一点。当质因数很多时时间上的差别就体现出来了,都是利用质因数分解定理,但是因为一点细节处理就导致时间上很大的差别。
void Eu(int n){ for(int i = 2;n-1;++i){ if(!(n%i)){ ans *= i - 1; n /= i; while(!(n%i)){ ans *= i;n/=i; } } } return ; }
这次学习的收获还有就是,又熟悉了一边素数表的线性求法:
//线性筛选素数的方法 for(int i = 2; i <4000;++i){ if(!not_prime[i]){ prime[p_len++] = i; } for(int j = 0; j < p_len;++j){ not_prime[i * prime[j]] = 1; if(!(i % prime[j])){ break; } } }
本来第一次看到这个求n(这里是40000)以内的素数表,实在去年暑期集训的时候在北交的acm对的大神blog里看到的,但是当时没理解怎么回事,后来有自己写过一次也没理解透是怎么回事,写的时候还是边参考这别人的代码便自己写,但是始终没想太明白为什么是对的,昨天在火车上回学校的时候好好想了一下这个原因为什么会是素数的,其实可以利用质因数唯一分解定理,和埃拉托斯特尼筛法就可以证明这个筛法是线性的,每一个被筛到的数,只被筛过一次。
质因数唯一分解定理说明任何 数 = 一个质数 prime[j] * 一个数i(可以为质数可以为非质数),当i %prime[j] = 0 时后面的就不用再算了其实后
面prime[j+1] * i 可以转化为 prime[j] * (大于i的数)的乘积;