【欧拉】 UVA 11426 GCD - Extreme (II)

这次不挂题目地址。。。因为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   |        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;
}

你可能感兴趣的:(数学,ACM)