poj2478 ——欧拉函数入门题

April 22, 2013
POJ Math

题目链接:http://poj.org/problem?id=2478 题目大意:   给你一个数列f(N),这个数列是由一系列不能约分的分数 a / b (0 < a < b <= n 且 (a,b) = 1)按照递增的顺序排列而成的。输入一个N,求这个数列中元素的个数。 题目思路:   因为题目只需要求出元素的个数,所以,把分母相同的放在一起,然后就发现规律了,其实就是求从2到 n 的欧拉函数的和。范围只有10^6,可以打表预处理。思路很清晰。因为要反复用欧拉函数,所以比较快的方法是用递推的方法求。   


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

     这个方法和筛法求素数比较类似,貌似就是那个思想。模拟一下什么就懂了。这里也用到了欧拉函数的性质:φ(n) = n * (1 - 1/p1) * (1 - 1/p2) * …… * (1 - 1/pk)。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
typedef long long int LL;
const int MAXN =  0x3f3f3f3f;
const int  MIN =  -0x3f3f3f3f;
const double eps = 1e-9;
const int dir[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,1},
  {1,1},{1,-1},{-1,-1}};

const int maxn = 1000000+10;
int f[maxn];
int main(void){
#ifndef ONLINE_JUDGE
  freopen("poj2478.in", "r", stdin);
#endif

  LL i, j;
  for (i = 1; i <= maxn; ++i) f[i] = i;
  for (i = 2; i <= maxn; i+=2) f[i] /= 2;
  for (i = 3; i <= maxn; i+=2){
    if (f[i] == i){
      for (j = i; j <= maxn; j+=i){
        f[j] = f[j] / i * (i - 1);
      }
    }
  }
  //printf("%d\n", f[5]);
  //for (i = 2; i <= 14; ++i) printf("f[%d] = %d\n", i, f[i]);
  //for (i = 2; i <= 14; ++i) printf("f[] = %d\n", f[i]);
  int n;
  while (~scanf("%d", &n)){
    if (!n) break; LL ans = 0;
    for (i = 2; i <= n; ++i){
      ans += f[i]; //printf("f[%d] = %d ", i, f[i]);
    }
    printf("%lld\n", ans);
  }

  return 0;
}

  写代码的时候还是遇到了一些问题,比如把改写成 j 的地方写成了 i ,还好很快找到了,以后这种错误从一开始就不要犯。   调试的时候发现一个很神奇的现象,不能解释啊。41行的注释,输出来 f[i] 的值都是0!42行的注释输出来就是正常的!好吧,原因到现在我还不知道……貌似是语言的细节?希望神牛路过指点啊~   另外,求的时候,没有必要每次都加一遍,也可以把f[]数组进一步处理,让f[n]保存前n项得欧拉函数的和,这样就可以直接输出f[n]的值了。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <cmath>
#include <algorithm>
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
typedef long long int LL;
const int MAXN =  0x3f3f3f3f;
const int  MIN =  -0x3f3f3f3f;
const double eps = 1e-9;
const int dir[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,1},
  {1,1},{1,-1},{-1,-1}};

const int maxn = 1000000+10;
LL f[maxn];
int main(void){
#ifndef ONLINE_JUDGE
  freopen("poj2478.in", "r", stdin);
#endif

  LL i, j;
  for (i = 1; i <= maxn; ++i) f[i] = i;
  for (i = 2; i <= maxn; i+=2) f[i] /= 2;
  for (i = 3; i <= maxn; i+=2){
    if (f[i] == i){
      for (j = i; j <= maxn; j+=i){
        f[j] = f[j] / i * (i - 1);
      }
    }
  }
  for (i = 3; i <= maxn; ++i){
    f[i] += f[i-1];
  }
  //printf("%d\n", f[5]);
  //for (i = 2; i <= 14; ++i) printf("f[%d] = %d\n", i, f[i]);
  //for (i = 2; i <= 14; ++i) printf("f[] = %d\n", f[i]);
  int n;
  while (~scanf("%d", &n)){
    if (!n) break; LL ans = 0;
    /*
    for (i = 2; i <= n; ++i){
      ans += f[i]; //printf("f[%d] = %d ", i, f[i]);
    }
    */
    printf("%lld\n", f[n]);
  }

  return 0;
}

  速度快了一点儿~这种预处理的方法学习一下。

comments powered by Disqus