Description
Input
Output
Sample Input
2 3 4 5 0
Sample Output
1 3 5 9
题意:给定一个数n,求小于或等于n的数中两两互质组成的真分数的个数。 表达的有点挫,很直接的欧拉函数的应用。
phi(x) 表示与x互质且小于x的正整数的个数。
直接见代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define see(x) cout<<#x<<":"<<x<<endl;
#define MAXN 1000005
using namespace std;
int prime[MAXN],phi[MAXN];
bool notp[MAXN];
void Prime(){
int i, j, np=0;
phi[1] = 0;
memset(notp,false,sizeof(notp));
for(i=2;i<MAXN;i++){
if(notp[i]==false){
prime[np++] = i;
phi[i] = i-1;
}
for(j=0;j<np&&i*prime[j]<MAXN;j++){
notp[i*prime[j]] = true;
if(i%prime[j]==0){
phi[i*prime[j]] = phi[i]*prime[j];
break;
}
else{
phi[i*prime[j]] = phi[i]*(prime[j]-1);
}
}
}
}
long long ans[MAXN];
int main(){
int n, i;
Prime();
ans[1] = 0;
for(i=2;i<MAXN;i++){
ans[i] = ans[i-1] + phi[i];
}
while(scanf("%d",&n)&&n){
cout<<ans[n]<<endl;
}
return 0;
}
这里要说到关于筛素数的方法。以下算法复杂度为O(n)
void Prime(){
int i, j, np=0;
memset(notp,false,sizeof(notp));
for(i=2;i<MAXN;i++){
if(notp[i]==false){
prime[np++] = i;
}
for(j=0;j<np&&i*prime[j]<MAXN;j++){
notp[i*prime[j]] = true;
if(i%prime[j]==0){ //O(n)的关键
break;
}
}
}
}
适时跳出,大大降低了算法复杂度。本质就是任意一个合数都有一个最小质因数,最终被赋值为true时,都是由其的最小质因数筛出来的,其被筛出来后也无需再关心其他数了。
原因如下:设 i 的最小质因数为p[j],则 i%p[j]==0,设k=i/p[j],w=i*p[j]=k*p[j]*p[j],w是由k*p[j]和p[j]共同筛出来的,对于比w更大的也有因子p[j]的数ww,则自然可以由后面的m*p[j](m>k)和p[j]共同筛出来了。
对筛素数的代码,略加几行代码,就可以顺带求出欧拉函数phi[x]了。用到了递推式:
对于p|x。若p2|x,则phi(x) = phi(x/p)*p;否则phi(x) = phi(x/p)*(p-1)