GCD - Extreme (II)
Given the value of N, you will have to find the value of G. The definition of G is given below:
Here GCD(i, j) means the greatest common divisor of integer i and integer j.
For those who have trouble understanding summation notation, the meaning of G is given in the
following code:
G=0;
for(i=1;i<N;i++)
for(j=i+1;j<=N;j++)
{
G+=gcd(i,j);
}
/Here gcd() is a function that finds
the greatest common divisor of the two
input numbers/
Input
The input file contains at most 100 lines of inputs. Each line contains an integer N (1 < N < 4000001).
The meaning of N is given in the problem statement. Input is terminated by a line containing a single
zero.
Output
For each line of input produce one line of output. This line contains the value of G for the corresponding
N. The value of G will fit in a 64-bit signed integer.
Sample Input
10
100
200000
0
Sample Output
67
13015
143295493160
题目:
给出n,求gcd(1,2)+gcd(1,3)+gcd(2,3)+gcd(1,4)+gcd(2,4)+gcd(3,4)+…+gcd(1,n)+gcd(2,n)+…+gcd(n-1,n)
分析:
令sum(n)=gcd(1,n)+gcd(2,n)+…+gcd(n-1,n),则所求结果ans(n)=sum(2)+sum(3)+…+sum(n)
只需求出sum(n),就可以推出所有答案:ans(n)=ans(n-1)+sum(n)。
接下来重点就是求sum(n):
注意到所有gcd(x,n)都是n的约数,可以按照这个约数进行分类,用g(n,i)表示满足g(x,n)=i且x<n的正整数个数,
则sum(n)=sum { i*g(n,i)|i是n的约数 } 。注意到gcd(x,n)=i的充要条件是gcd(x/i,n/i)=1,因此满足条件的x/i有phi(n/i)个(欧拉函数),说明g(n,i)=phi(n/i)。
由于时间限制,同素数筛选法,我们需要对于每个i枚举它的倍数n并更新sum(n),这些都在预处理中完成。
代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 4000001
typedef long long LL;
LL a[N], b[N], dp[N];
int main()
{
for(int i=2; i<N; i++)///欧拉打表;
{
if(!a[i])
{
for(int j=i; j<N; j+=i)
{
if(!a[j]) a[j]=j;
a[j]=a[j]/i*(i-1);
}
}
}
for(int i=1; i<N; i++)///[1,n-1]中所有的数与n的gcd的和
for(int j=i*2; j<N; j+=i)
b[j] += a[j/i]*i;
for(int i=2; i<N; i++)
dp[i]=dp[i-1]+b[i];
int n;
while(scanf("%d", &n), n)
{
printf("%lld\n", dp[n]);
}
return 0;
}
既然说到欧拉函数,那就多写一点。
欧拉函数就是指:对于一个正整数n,小于n且和n互质的正整数(包括1)的个数,记作φ(n) 。
欧拉函数的通式:φ(n)=n * (1-1/p1) * (1-1/p2) * (1-1/p3) * (1-1/p4)……(1-1/pn), 其中p1, p2……pn为n的所有质因数,n是不为0的整数。φ(1)=1(唯一和1互质的数就是1本身)。
我们根据欧拉函数的通式,即可得到如下代码:
// 求单个数的欧拉函数
int p(int n)
{
int ans=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
ans-=ans/i; //等价于通项,把n乘进去
while(n%i==0)//确保下一个i是n的素因数
{
n/=i;
}
}
}
if(n>1)ans-=ans/n; //最后可能还剩下一个素因数没有除
return ans;
}
但是一般我们做题要求都比较多,这个时候我们要是在一个一个枚举,很大几率会超时,因此我们借鉴素数打表的思想,自然而然想起了欧拉打表。
//埃拉托斯特尼筛求欧拉函数
void euler(int n)
{
for (int i=1;i<=n;i++) phi[i]=i;
for (int i=2;i<=n;i++)
{
if (phi[i]==i)//这代表i是质数
{
for (int j=i;j<=n;j+=i)
{
phi[j]=phi[j]/i*(i-1);//把i的倍数更新掉
}
}
}
}