UVA11426 GCD

来源:互联网 发布:淘宝小二是什么意思 编辑:程序博客网 时间:2024/06/07 19:55

题目描述:给定n(1 <= n <= 4000,000),求gcd(1 , 2)  + gcd(1 , 3) + gcd(1 , 4) +... + gcd(1, n)

                                                                                              +gcd(2 , 3) + gcd(2 , 4) + ... + gcd(2 , n)

                                                                                                                   +gcd(3 , 4) + ... + gcd(3 , n)

                                                                                                                                      +......

                                                                                                                                             +gcd(n - 1 , n)

思路:首先计算x,gcd(1 , x) + gcd(2 , x ) + ... + gcd(x - 1 , x)的值,他的值就等于Σ d * {y | gcd(y , x) == d},因为gcd(y , x) == d 等价于 gcd(y / d , x / d) = = 1,所以原式等价于

Σ d * phi(x / d) , 其中phi(x / d)表示x / d的欧拉函数,也就是说,对于从1到n每个数x,枚举它的每个因数d ,然后用累加所有 d * phi(x / d),再将n个数算出来的结果加到一起,但这样速度不够快,可以使用类似欧拉函数的求法,枚举因数找他的倍数,时间可以优化到O(nlogn)


收获:1、对于gcd(x , y)的计数问题,因为有gcd(y , x) == d 等价于 gcd(y / d , x / d) = = 1,所以gcd计数与欧拉函数有天然的联系

            2、凡是枚举每个数的因数算贡献的问题,只要通过变成枚举倍数,时间都会变成O(nlogn)

#pragma warning(disable:4786)#pragma comment(linker, "/STACK:102400000,102400000")#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<stack>#include<queue>#include<map>#include<set>#include<vector>#include<cmath>#include<string>#include<sstream>#include<bitset>#define LL long long#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)#define mem(a,x) memset(a,x,sizeof(a))#define lson l,m,x<<1#define rson m+1,r,x<<1|1using namespace std;const int INF = 0x3f3f3f3f;const int mod = 1e9 + 7;const double PI = acos(-1.0);const double eps=1e-6;const int maxn = 4e6 + 50;LL phi[maxn];void Euler(int n){    mem(phi , 0);    phi[1] = 1;    for(int i = 2 ; i <= n ; i++){        if(phi[i])      continue;        for(int j = i ; j <= n ; j += i){            if(!phi[j])     phi[j] = j;            phi[j] = phi[j] / i * (i - 1);        }    }}int main(){    Euler(4e6 + 5);    int n;    while(scanf("%d" , &n) != EOF){        if(!n)      break;        LL ans = 0;        for(int i = 1 ; i <= n ; i++){            for(int j = 2 * i ; j <= n ; j += i){                ans += i * phi[j / i];            }        }        printf("%lld\n",ans);    }    return 0;}


0 0