[UVa 11426] GCD - Extreme (II) (数论 + 脑洞 + 技巧)

UVa - 11426

给定一个 N,数对 i,j <= N且 i < j
求所有数对的 gcd(i,j)的和
其中 N <= 4e6

这是大白 p125的例题
首先按题目描述两个for肯定是不行的
所以我们反过来考虑,一个数是多少对数的 gcd
换句话来说,就是一个数被加了几次
于是我们枚举这个 gcd i
设有数对 aba<b 使得 gcd(a,b)=i
a=aib=bi
其中 ab 互质且 a<b
那么对于一个 b ,小于他的数有多少个与它互质呢
答案是 ϕ(b)
于是我们就能得到对于一个数 b ,有多少个 a 使得 gcd(a,b)=i
时间复杂度 O(N(ln(n)+C)) ,其中 C 是欧拉常数
所以问题就得到解决了

关于一个数的因子或 gcd ,通过枚举因子来计算通常比分解一个数来得更方便

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define Pow2(a) (a*a)

const int maxn=4e6+10;;
int N;
int phi[maxn];
LL sum[maxn],ans[maxn];

void init_phi(int);

int main()
{
    init_phi(maxn);
    for(int i=1; i<maxn; i++) for(int j=i*2; j<maxn; j+=i)
        sum[j]+=(LL)i*phi[j/i];
    ans[2]=sum[2];
    for(int i=3; i<maxn; i++) ans[i]=ans[i-1]+sum[i];
    while(~scanf("%d", &N)&&N)
    {
        printf("%lld\n", ans[N]);
    }
    return 0;
}

void init_phi(int Lim)
{
    for(int i=1; i<Lim; i++) phi[i]=i;
    for(int i=2; i<Lim; i++)
    {
        if(phi[i]<i) continue;
        for(int j=i; j<Lim; j+=i) phi[j]=phi[j]/i*(i-1);
    }
}

你可能感兴趣的:(uva)