poj 2478(欧拉函数打表)

题目链接:http://poj.org/problem?id=2478

题意:求法雷级数Fn中有多少个分数

法雷级数Fn定义:

      F2 = {1/2}

      F3 = {1/3 , 1/2 , 2/3}

                    F4 = {1/4 , 1/3 , 1/2 , 2/3 , 3/4}

                    F5= {1/5 , 1/4 , 1/3 , 2/5 , 1/2 , 3/5 , 2/3 , 3/ 4 , 4/5}

思路:很明显答案是分别 找2,3.....n中欧拉函数之和。

 我们会想到跑一遍所有的欧拉函数,再前缀和。但是n是106,用时间复杂度O(n√n)求每个数的欧拉函数是会超时的。

这里有一个时间复杂度约O(nln n)的递推求欧拉函数。

for(int i=1;i<1000010;i++)
        ph[i]=i;
for(int i=2;i<1000010;i+=2)
        ph[i]=i/2;
for(int i=3;i<1000010;i+=2)
    {
        if(ph[i]==i)
        {
            for(int j=i;j<1000010;j+=i)
                ph[j]=ph[j]/i*(i-1);
        }
    }

思路就是,当φ(n)=n-1时,n是素数。所以在遍历过程中如果遇到欧拉函数等于自身的,那么说明该数是素数,把这个数的欧拉函数改变,

同时也把该素因子整除的数改变。

代码中是先令欧拉函数等于本身,先将偶数筛掉一个素因子2。  现在素数只可能大于2的奇数,再直接筛奇数,将它的倍数中的这个素因子筛掉,这里和一般的一样 乘以 (i-1)/i。所以会找所有素数,且每个数都会筛掉自己的所以素因子,从而得到欧拉函数。

 完整代码:

#include<stdio.h>
#include<string.h>
typedef long long ll;
ll ph[1000010];
int main()
{
    for(int i=1;i<1000010;i++)
        ph[i]=i;
    for(int i=2;i<1000010;i+=2) 
        ph[i]=i/2;
    for(int i=3;i<1000010;i+=2)//打表 
    {
        if(ph[i]==i)
        {
            for(int j=i;j<1000010;j+=i)
                ph[j]=ph[j]/i*(i-1);
        }
    }
    for(int i=3;i<1000010;i++)//前缀和 
        ph[i]+=ph[i-1];
    ll n;
    while(scanf("%lld",&n)!=EOF&&n)
    {
        printf("%lld
",ph[n]);
    }    
    return 0;
}
原文地址:https://www.cnblogs.com/xiongtao/p/10864227.html