【题目链接】
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