题意:求 n 以内的每两个数的最大公约数gcd(a, b)的和
解题思路:假设m和n 是互质的两个数(m < n),那么gcd(m, n) = 1, 当题目中的 i, j 循环到m,n 时,g += 1,依此,当 i = k*m, j = k*n (k为整数),g += k 。
由此我们可以得出:结果 (g) 的变化是由互质的数变化引起的,所以我们的目的就是先求出 给定范围内的所有的互质的数,然后加上他们的倍数,最后求和即可。
假设和 n 互质的数有 x 个,假设为x(k) (k <= x),那么在 i、j 循环到 x(k)、n 时结果会增加x,循环到(p*x(k), p*n) 时结果就会增加p*x。那么我们用table[i]记录各种x、n 在满足 x*n = i 时会增加多少结果,那么最后我们要输出的就是table[2] + table[3] + ... + table[N]。
其中不得不提的是找 n 以内的与 n 互质的数(也就是求欧拉函数),然后求相应的table[x*n]。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 6 using namespace std; 7 8 typedef long long LL; 9 10 const int maxv = 4000002; 11 LL table[maxv]; //打表 12 int euler[maxv]; // 偶拉函数值 13 14 void eul() 15 { 16 int i, j; 17 memset(table, 0, sizeof(table)); 18 for(i = 1; i < maxv; ++i) euler[i] = i; 19 for(i = 2; i < maxv; ++i) 20 { 21 if(euler[i] == i) //对每个素数的倍数求欧拉函数表 22 for(j = i; j < maxv; j += i) 23 euler[j] = euler[j] / i * (i-1); //每遇到一个素数因子,就进行处理 24 for(j = 1; j*i < maxv; j++) 25 table[j*i] += j * euler[i]; //j倍 26 } 27 for(i = 1; i < maxv; ++i) 28 table[i] += table[i-1]; //n的值等于本身的值加上前面的值,因为是求和 29 } 30 31 int main() 32 { 33 int n; 34 eul(); 35 while(cin >> n && n) 36 { 37 cout << table[n] << endl; 38 } 39 return 0; 40 }