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