这次不挂题目地址。。。因为UVa的感觉。。。好吧我还是贴题目吧。
Problem J
GCD Extreme (II)
Input: Standard Input
Output: Standard Output
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 Output for Sample Input
10 100 200000 0
| 67 13015 143295493160
|
Problemsetter: Shahriar Manzoor
Special Thanks: SyedMonowarHossain
题目要求的是范围内的所有gcd( i , j )( j > i ) 和。。。我们的第一反应自然是去求 gcd( i , n) 然后预处理成o(1)输出。。。
然而这时候发现数字太大不能直接求。
看题解的时候给的公式是这样的
for(int i=1;i<=4000000;i++)
{
for(int j=2*i;j<=4000000;j+=i)
{
ans[j]+=oura[j/i]*i;
}
ans[i]+=ans[i-1];
}
然而不理解,之后去问了人之后得到了这个的证明方法
-------------------------------------------分割线-----------------------------------------------------
首先我们把gcd打成表。
| 1 2 3 4 5 6 7 8
-----+-------------------------------------------------------
1 | 1 1 1 1 1 1 1 1
2 | 1 2 1 2 1 2 1 2
3 | 1 1 3 1 1 3 1 1
4 | 1 2 1 4 1 2 1 4
5 | 1 1 1 1 5 1 1 1
6 | 1 2 3 2 1 6 1 2
7 | 1 1 1 1 1 1 7 1
8 | 1 2 1 4 1 2 1 8
显然,黄底的部分就是我们找的答案
这时候我们把2的倍数的取出来看看的话
| 1 2 3 4 5 6 7 8
-----+-------------------------------------------------------
1 | 1 1 1 1 1 1 1 1
2 | 1 2 1 2 1 2 1 2
3 | 1 1 3 1 1 3 1 1
4 | 1 2 1 4 1 2 1 4
5 | 1 1 1 1 5 1 1 1
6 | 1 2 3 2 1 6 1 2
7 | 1 1 1 1 1 1 7 1
8 | 1 2 1 4 1 2 1 8
你会发现如果再对每项除以2就是和刚刚一样的图了
此时的所有2变成1,我们用欧拉函数便可很快的将它们取出来
同理可推广到所有数字,因此有了之前的公式。。。
-------------------------------------------分割线-----------------------------------------------------
代码其实可以不用了吧。。。毕竟刚刚讲的挺详细了(是这样吗?)
而且我的代码风格感觉并不是很好。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define LL long long
using namespace std;
int oura[4000010];
int prime[300000],cont;
long long ans[4000010];
void init()
{
cont=0;
for(int i=2;i<=4000000;i++)
{
if(!oura[i])
{
prime[cont++]+=i;
oura[i]=i-1;
}
for(int j=0;j<cont;j++)
{
if(prime[j]>4000000/i) break;
if(i%prime[j]!=0)
oura[prime[j]*i]=oura[i]*(prime[j]-1);
else
{
oura[prime[j]*i]=oura[i]*prime[j];
break;
}
}
}
ans[0]=0;
for(int i=1;i<=4000000;i++)
{
for(int j=2*i;j<=4000000;j+=i)
{
ans[j]+=oura[j/i]*i;
}
ans[i]+=ans[i-1];
}
}
int main()
{
init();
int n;
while(~scanf("%d",&n) && n)
{
printf("%lld\n",ans[n]);
}
return 0;
}