【codeforces 870F】Paths

Description

You are given a positive integer n. Let's build a graph on vertices 1, 2, ..., n in such a way that there is an edge between vertices u and v if and only if gcd(u,v)≠1. Let d(u, v) be the shortest distance between u and v, or 0 if there is no path between them. Compute the sum of values d(u, v) over all 1 ≤ u < v ≤ n.The gcd (greatest common divisor) of two positive integers is the maximum positive integer that divides both of the integers.

Input

Single integer n (1 ≤ n ≤ 107).

Output

Print the sum of d(u, v) over all 1 ≤ u < v ≤ n.

Examples

input
6
output
8
input
10
output
44

Note

All shortest paths in the first example:

There are no paths between other pairs of vertices.The total distance is 2 + 1 + 1 + 2 + 1 + 1 = 8. 

题意:
给定数字n,建立一个无向图。对于所有1~n之间的数字,当数字gcd(u,v)≠1时将u、v连一条边,边权为1。d(u, v)表示u到v的最短路,求所有d(u, v)的和,其中1 ≤ u < v ≤ n。
 
分析:
对于1以及所有大于n/2的的质数,与其他数字均不联通,直接剔除。
对于剩下的数字:
1.当gcd(u,v)≠1时,d(u, v)==1。即对于数字u,小于u且d(u, v)==1的数字个数为x - 1 - φ(x)。
2.令p[u]表示数字u的最小质因子,则当p[u]·p[v] ≤ n时,d(u, v)==2。维护数组num、sum,num[i]代表最小质因子为i的数字个数,sum数组为num数组的前缀和。统计Σnum[i]·sum[n/i]可以覆盖所有p[u]·p[v] ≤ n的情况,其中减去自身与自身被统计的情况,剩下的所有数对都被统计了两次,其中包含gcd(u,v)≠1的情况,需进行相应处理,详见代码。
3.剩下的数对最短路一定为3,因为 u→2·p[u]→2·p[v]→v这条路一定存在。可通过数对总数减去d(u, v)==1与d(u, v)==2的情况得到。
 
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e7+5;
 7 int n,m,tot,now,pri[N],p[N],phi[N],num[N],sum[N]; 
 8 LL one,two,three;
 9 int main()
10 {
11     scanf("%d",&n);
12     phi[1]=1;
13     for(int i=2;i<=n;i++)
14     {
15         if(!p[i]){p[i]=pri[++tot]=i;phi[i]=i-1;}
16         for(int j=1;j<=tot;j++)
17         {
18             if(i*pri[j]>n)break;
19             p[i*pri[j]]=pri[j];
20             if(i%pri[j]==0){phi[i*pri[j]]=phi[i]*pri[j];break;}
21             else phi[i*pri[j]]=phi[i]*(pri[j]-1);
22         }
23     } 
24     for(int i=2;i<=n;i++)one+=i-1-phi[i];
25     for(int i=2;i<=n;i++)num[p[i]]++;
26     for(int i=2;i<=n;i++)sum[i]=sum[i-1]+num[i];
27     for(int i=2;i<=n;i++)two+=1ll*num[i]*sum[n/i];
28     for(int i=2;i<=n;i++)if(1ll*p[i]*p[i]<=n)two--;
29     two=two/2-one;m=n-1;
30     for(int i=tot;i>=1;i--)
31         if(pri[i]*2>n)m--;
32         else break;
33     three=1ll*m*(m-1)/2-one-two;
34     printf("%I64d\n",one+two*2+three*3);
35     return 0;
36 }
View Code

 

posted @ 2018-01-10 13:29  Zsnuo  阅读(191)  评论(0编辑  收藏  举报