bzoj 2818 Gcd(欧拉函数 | 莫比乌斯反演)

【题目链接】

    http://www.lydsy.com/JudgeOnline/problem.php?id=2818

【题意】

  问(x,y)为质数的有序点对的数目。

【思路一】

  定义f[i]表示i之前(x,y)=1的有序点对的数目,则有递推式:

          f[1]=1

          f[i]=f[i-1]+phi[i]*2

  我们依次枚举小于n的所有素数,对于素数t,(x,y)=t的数目等于(x/t,y/t),即f[n/t]。

【代码一】

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 typedef long long ll;
 7 const int N = 1e7+10;
 8 
 9 int su[N],tot,phi[N];
10 ll f[N];
11 
12 void get_pre(int n)
13 {
14     phi[1]=1;
15     for(int i=2;i<=n;i++) if(!phi[i]) {
16         su[++tot]=i;
17         for(int j=i;j<=n;j+=i) {
18             if(!phi[j]) phi[j]=j;
19             phi[j]=phi[j]/i*(i-1);
20         }
21     }
22     f[1]=1;
23     for(int i=2;i<=n;i++) f[i]=f[i-1]+2*phi[i];
24 }
25 
26 int n;
27 
28 int main()
29 {
30     scanf("%d",&n);
31     get_pre(n);
32     ll ans=0;
33     for(int i=1;i<=tot;i++)
34         ans+=f[n/su[i]];
35     printf("%lld
",ans);
36     return 0;
37 }

【思路二】

  其它思路一样,不同的是使用莫比乌斯反演计算(x,y)=1的数目,累计答案的时间复杂度为O(n sqrt(n))

  推倒过程:

【代码二】

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 typedef long long ll;
 7 const int N = 1e7+10;
 8 
 9 int su[N],mu[N],tot,vis[N];
10 
11 void get_mu(int n)
12 {
13     mu[1]=1;
14     for(int i=2;i<=n;i++) {
15         if(!vis[i]) {
16             su[++tot]=i;
17             mu[i]=-1;
18         }
19         for(int j=1;j<=tot&&su[j]*i<=n;j++) {
20             vis[i*su[j]]=1;
21             if(i%su[j]==0) mu[i*su[j]]=0;
22             else mu[i*su[j]]=-mu[i];
23         }
24     }
25     for(int i=1;i<=n;i++) mu[i]+=mu[i-1];
26 }
27 
28 int n;
29 
30 ll calc(int n)
31 {
32     int i,last; ll ans=0;
33     for(int i=1;i<=n;i=last+1) {
34         last=n/(n/i);
35         ans+=(ll)(n/i)*(n/i)*(mu[last]-mu[i-1]);
36     }
37     return ans;
38 }
39 
40 int main()
41 {
42 //    freopen("in.in","r",stdin);
43 //    freopen("out.out","w",stdout);
44     scanf("%d",&n);
45     get_mu(n);
46     ll ans=0;
47     for(int i=1;i<=tot;i++)
48         ans+=calc(n/su[i]);
49     printf("%lld
",ans);
50     return 0;
51 }

UPD.16/4/8

  另外莫比乌斯反演还有一种O(n)预处理O(sqrt(n))查询的做法 click Here

原文地址:https://www.cnblogs.com/lidaxin/p/5337479.html