FZU 1969 GCD Extreme UESTC 1723 吴神的大脑

Accept: 77    Submit: 136
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

Given the value of N, you will have to find the value of G. 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 20000 lines of inputs. Each line contains an integer N (1<N <1000001). 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

参考思想
http://hi.baidu.com/aekdycoin/item/8095ba4eebb2d3eba5c066d2
代码参考
http://blog.csdn.net/duanxian0621/article/details/7847514
G=0;

for(k=1;k< N;k++)

for(j=i+1;j<=N;j++)

{

G+=gcd(k,j);

}
显然如果O(n^2*gcd复杂度) ,一组数据就够你挂的了.
因此我们必须优化优化再优化

观察显然可以得到:
G=
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)


于是得到G的公式



为什么写成这种形式?是为了求解方便

显然如果依然暴力套肯定还是超时

于是我们考虑优化,因为这个公式还是很有特点的
如果我们可以很快的得到下面的值(假设是G(n)),那么上面的公式通过预处理来得到解答,最后查询只需要O(1)


这个如何计算?
由于gcd(n,i) | n
所以gcd(n,i)=k,k|n
于是我们可以先得到n的所有因子,然后利用gcd(n,i)=k--->gcd(n/k,i/k)=1来求解
即先求出满足gcd(n,i)=k的个数(Euler(n/k)),然后把个数直接乘上k就是这一部分的和,同理其他部分

显然如果暴力分解肯定超时,于是考虑预处理
1.预处理出1..1000000的Euler()
2.预处理出1..1000000的G(n)
3.预处理区间和
然后就可以了
预处理Euler()并不难,详见:
http://hi.baidu.com/aekdycoin/blog/item/7055063fa5011a3a70cf6c30.html

至于处理G(n),必须使用到预处理因子和的类似技巧.

预处理因子和只需要用2个循环搞定(前提是规模不大,百万级别还是可以接受的)
第1个循环,i,枚举1..sqrt(N)
第2个循环,j,枚举j=k*i,在保证i*i<j的情况下,我们根据因子对称性可以知道
dp[j]+=i+j/i;考虑到i*i=j的情况下我们只需要加一次,所以这时候dp[j]+=i;

而类似的,这题也可以这么做,假设i为j的因子,那么显然有
1.i*i<j
G[j]+=Euler(i)*(j/i)+Euler(j/i)*i
2.i*i==j
G[j]+=Euler(i)*i

 



学到了快速同时求很多欧拉函数的方法
//还有利用欧拉函数求求1-n中所有数的最大公约数之和)

#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #define lson l,m,k<<1 #define rson m+1,r,k<<1|1 #define N 1000001 using namespace std; __int64 phi[N]; __int64 ans[N]; void init() { int i,j,k; for(i=2;i<N;i++) phi[i]=i; for(i=2;i<N;i++) if(phi[i]==i) for(j=i;j<N;j+=i) phi[j]=phi[j]/i*(i-1); for(i=2;i<N;i++) ans[i]=phi[i]; for(i=2;i<=1000;i++) { ans[i*i]+=phi[i]*i; for(j=i*i+i,k=i+1;j<N;j+=i,k++) ans[j]+=phi[k]*i+k*phi[i]; } for(i=3;i<N;i++) ans[i]+=ans[i-1]; } int main() { init(); int n; while(scanf("%d",&n),n) { printf("%I64d\n",ans[n]); } return 0; }

 

posted on 2012-09-22 09:51  江财小子  阅读(696)  评论(0编辑  收藏  举报