poj2480 Longge's problem ——积性函数入门题

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

题目大意:

  给定一个数字N,求∑gcd(i, N) 1<=i <=N 的值。

题目思路:

  x是一个数字,m与n互素,则gcd(x,m*n) = gcd(x, m) * gcd(x, n) 令g(y) = gcd(x, y) 那么g(y)是一个积性函数。令f(N) = ∑gcd(i, N) 

  满足gcd(x, n) = 1 的个数是欧拉函数φ(n),那么可以知道,满足gcd(x, n) = p 的个数可以这么求:x 和 n 同时除以 p ,那么gcd(x/p, n/p) = 1 ,那么个数就是φ(n/p)。

  分解N = p1^a1 * p2^a2 * …… *pn^an ,则f(N) = f(p1^a1 * p2^a2 * …… *pn^an) = f(p1^a1) * f(p2^a2) * …… * f(pn^an);

  可以枚举pi^ai的因数,对于f(pi^ai) = 1 * φ(pi^ai) + pi * φ(pi^(ai-1)) + pi^2 * φ(pi^(ai-2)) + …… + pi^(ai-1) * φ(pi) + pi^ai * φ(1);

  根据φ(pi^ai) = pi^ai - pi^(ai-1),那么可以化简上面的式子:f(pi^ai) = ai * pi^ai + ai * pi^(ai-1) + pi^ai = pi^ai * (ai + ai/pi + 1);

  所以,f(N) = N * (a1 + a1/p1 + 1) * (a2 + a2/p2 + 1) * …… * (an + an/pn + 1)。

  这题当然不是自己想出来的,但是学习了一下积性函数,看的神牛的解题代码:http://hi.baidu.com/lydrainbowcat/item/66a0c9088f876f12acdc70ba 说来这神牛是我的学弟,比我小一届貌似,一个高中的,竟然还都是省理的,唉……惭愧

  不说了,都是泪……什么也不想了,专注刷题。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cctype>
 6 #include <stack>
 7 #include <queue>
 8 #include <map>
 9 #include <set>
10 #include <vector>
11 #include <cmath>
12 #include <algorithm>
13 #define lson l, m, rt<<1
14 #define rson m+1, r, rt<<1|1
15 using namespace std;
16 typedef long long int LL;
17 const int MAXN =  0x3f3f3f3f;
18 const int  MIN =  -0x3f3f3f3f;
19 const double eps = 1e-9;
20 const int dir[8][2] = {{0,1},{1,0},{0,-1},{-1,0},{-1,1},
21   {1,1},{1,-1},{-1,-1}};
22 LL n, i, j, ans;
23 int main(void){
24 #ifndef ONLINE_JUDGE
25   freopen("poj2480.in", "r", stdin);
26 #endif
27   while (~scanf("%lld", &n)){
28     ans = n;
29     for (i = 2; i * i <= n; ++i){
30       j = 0;
31       if (n % i == 0){
32         while (n % i == 0){
33           j++; n /= i;
34         }
35         ans /= i; ans = ans * (i * j - j + i);
36       }
37     }
38     if (n > 1){
39       ans /= n; ans = ans * (n - 1 + n);
40     }
41     printf("%lld\n", ans);
42   }
43 
44   return 0;
45 }

  开始用cin,cout超时一次,后来发现 i ,j,不应该用 int ,应该用LL,这才过了,32ms

  另外一份比较快的代码:谢谢Wahaha_hnu llw学长,还没看懂,继续有时间继续研究

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 
 5 using namespace std;
 6 
 7 typedef __int64 LL;
 8 #define N 70000
 9 
10 LL n;
11 LL f[N],prime[N],primeNum;
12 LL a[N],b[N],cnt, ans;
13 bool vis[N];
14 void sieve()
15 {
16     int i,j;
17     for(i=0;i<N;i++) f[i]=1;
18     f[0]=f[1]=0;
19     for(i=2;i*i<N;i++)
20         if(f[i])
21             for(j=i*i;j<N;j+=i)
22                 f[j]=0;
23     j=0;
24     for(i=2;i<N;i++)
25         if(f[i])
26             prime[j++]=i;
27     primeNum=j;
28 }
29 void dfs(int k,LL x)
30 {
31     int i,j;
32     LL y,t;
33     if(k==cnt)
34     {
35         y=n/x;
36         for(i=0;i<cnt;i++)
37             if(!vis[i])
38                 y-=y/a[i];
39         ans+=x*y;
40 //        printf("x=%I64d y=%I64d\n",x,y);
41         return ;
42     }
43     t=1;
44     for(i=0;i<b[k];i++)
45     {
46         dfs(k+1, x*t);
47         t*=a[k];
48     }
49     vis[k]=1;
50     dfs(k+1, x*t);
51     vis[k]=0;
52 }
53 int main()
54 {
55     int i,j,k;
56 
57     sieve();
58     while(scanf("%I64d",&n)==1)
59     {
60         LL x=n;
61         cnt=0;
62         for(i=0;i<primeNum &&prime[i]*prime[i]<=x;i++)
63             if(x%prime[i]==0)
64             {
65                 a[cnt]=prime[i], b[cnt]=0;
66                 while(x%prime[i]==0)
67                     b[cnt]++, x/=prime[i];
68                 cnt++;
69             }
70         if(x>1) a[cnt]=x, b[cnt]=1, cnt++;
71 //        printf("cnt=%I64d\n",cnt);
72 //        for(i=0;i<cnt;i++)
73 //            printf("%I64d %I64d\n",a[i], b[i]);
74 
75         ans=0;
76         memset(vis,0,sizeof(vis));
77         dfs(0,1);
78         printf("%I64d\n",ans);
79     }
80   return 0;
81 }

  这份代码可以16ms过的……

posted on 2013-04-21 23:45  aries__liu  阅读(223)  评论(0编辑  收藏  举报